Merge RQ3A.210705.001 to aosp-master - DO NOT MERGE

Merged-In: I7b187c5e79bf4531bdffc7411a033a7c20a54294
Merged-In: I89cda19ca8521445e1608efbbd07c889cca59601
Merged-In: I89cda19ca8521445e1608efbbd07c889cca59601
Change-Id: I183701b2c03dc63d19c98779388c0b99bae9a4ce
diff --git a/Android.bp b/Android.bp
index 9e1df13..815e766 100644
--- a/Android.bp
+++ b/Android.bp
@@ -1,3 +1,33 @@
+package {
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+// Added automatically by a large-scale-change that took the approach of
+// 'apply every license found to every target'. While this makes sure we respect
+// every license restriction, it may not be entirely correct.
+//
+// e.g. GPL in an MIT project might only apply to the contrib/ directory.
+//
+// Please consider splitting the single license below into multiple licenses,
+// taking care not to lose any license_kind information, and overriding the
+// default license using the 'licenses: [...]' property on targets as needed.
+//
+// For unused files, consider creating a 'fileGroup' with "//visibility:private"
+// to attach the license to, and including a comment whether the files may be
+// used in the current project.
+// See: http://go/android-license-faq
+license {
+    name: "hardware_interfaces_license",
+    visibility: [":__subpackages__"],
+    license_kinds: [
+        "SPDX-license-identifier-Apache-2.0",
+        "SPDX-license-identifier-NCSA",
+    ],
+    license_text: [
+        "NOTICE",
+    ],
+}
+
 hidl_package_root {
     name: "android.hardware",
     use_current: true,
@@ -15,7 +45,6 @@
 cc_defaults {
     name: "VtsHalTargetTestDefaults",
     defaults: [
-        "vts_target_tests_defaults",
         "hidl_defaults",
     ],
 
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..d97975c
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,3 @@
+third_party {
+  license_type: NOTICE
+}
diff --git a/MODULE_LICENSE_APACHE2 b/MODULE_LICENSE_APACHE2
deleted file mode 100644
index e69de29..0000000
--- a/MODULE_LICENSE_APACHE2
+++ /dev/null
diff --git a/OWNERS b/OWNERS
index 433bbb7..3a1a038 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,9 +1,14 @@
 per-file *.hal,*.aidl,OWNERS = set noparent
-per-file *.hal,*.aidl,OWNERS = elsk@google.com,malchev@google.com,smoreland@google.com
+per-file *.hal,*.aidl,OWNERS = devinmoore@google.com,elsk@google.com,malchev@google.com,smoreland@google.com
 
+# Android Native API Council
+devinmoore@google.com
 elsk@google.com
-maco@google.com
 malchev@google.com
 smoreland@google.com
-yim@google.com  # vts tests
-guangzhu@google.com # vts tests
+
+# historical/backup
+maco@google.com
+
+# vts tests
+guangzhu@google.com
diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg
index 6740bb5..2116e21 100644
--- a/PREUPLOAD.cfg
+++ b/PREUPLOAD.cfg
@@ -4,6 +4,7 @@
 [Builtin Hooks]
 bpfmt = true
 clang_format = true
+aidl_format = true
 
 [Hook Scripts]
 aosp_hook_confirmationui = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} confirmationui
diff --git a/atrace/1.0/Android.bp b/atrace/1.0/Android.bp
index c7e8d04..69d25f4 100644
--- a/atrace/1.0/Android.bp
+++ b/atrace/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.atrace@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAtraceDevice.hal",
diff --git a/atrace/1.0/default/Android.bp b/atrace/1.0/default/Android.bp
index 4bbbdb3..766ef36 100644
--- a/atrace/1.0/default/Android.bp
+++ b/atrace/1.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.atrace@1.0-service",
     defaults: ["hidl_defaults"],
diff --git a/atrace/1.0/default/AtraceDevice.cpp b/atrace/1.0/default/AtraceDevice.cpp
index 4e82b0a..9f0c4c4 100644
--- a/atrace/1.0/default/AtraceDevice.cpp
+++ b/atrace/1.0/default/AtraceDevice.cpp
@@ -16,6 +16,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/stringprintf.h>
 
 #include "AtraceDevice.h"
 
@@ -39,15 +40,11 @@
         // gfx
         {
                 "gfx",
-                {"Graphics",
-                 {{"/sys/kernel/debug/tracing/events/mdss/enable", false},
-                  {"/sys/kernel/debug/tracing/events/sde/enable", false},
-                  {"/sys/kernel/debug/tracing/events/mali_systrace/enable", false}}},
+                {"Graphics", {{"mdss", false}, {"sde", false}, {"mali_systrace", false}}},
         },
         {
                 "ion",
-                {"ION allocation",
-                 {{"/sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable", false}}},
+                {"ION allocation", {{"kmem/ion_alloc_buffer_start", false}}},
         },
 };
 
@@ -65,16 +62,31 @@
     return Void();
 }
 
+AtraceDevice::AtraceDevice() {
+    struct stat st;
+
+    tracefs_event_root_ = "/sys/kernel/tracing/events/";
+    if (stat(tracefs_event_root_.c_str(), &st) != 0) {
+        tracefs_event_root_ = "/sys/kernel/debug/tracing/events/";
+        CHECK(stat(tracefs_event_root_.c_str(), &st) == 0) << "tracefs must be mounted at either"
+                                                              "/sys/kernel/tracing or "
+                                                              "/sys/kernel/debug/tracing";
+    }
+}
+
 Return<::android::hardware::atrace::V1_0::Status> AtraceDevice::enableCategories(
-    const hidl_vec<hidl_string>& categories) {
+        const hidl_vec<hidl_string>& categories) {
     if (!categories.size()) {
         return Status::ERROR_INVALID_ARGUMENT;
     }
+
     for (auto& c : categories) {
         if (kTracingMap.count(c)) {
             for (auto& p : kTracingMap.at(c).paths) {
-                if (!android::base::WriteStringToFile("1", p.first)) {
-                    LOG(ERROR) << "Failed to enable tracing on: " << p.first;
+                std::string tracefs_event_enable_path = android::base::StringPrintf(
+                        "%s%s/enable", tracefs_event_root_.c_str(), p.first.c_str());
+                if (!android::base::WriteStringToFile("1", tracefs_event_enable_path)) {
+                    LOG(ERROR) << "Failed to enable tracing on: " << tracefs_event_enable_path;
                     if (p.second) {
                         // disable before return
                         disableAllCategories();
@@ -91,10 +103,13 @@
 
 Return<::android::hardware::atrace::V1_0::Status> AtraceDevice::disableAllCategories() {
     auto ret = Status::SUCCESS;
+
     for (auto& c : kTracingMap) {
         for (auto& p : c.second.paths) {
-            if (!android::base::WriteStringToFile("0", p.first)) {
-                LOG(ERROR) << "Failed to disable tracing on: " << p.first;
+            std::string tracefs_event_enable_path = android::base::StringPrintf(
+                    "%s%s/enable", tracefs_event_root_.c_str(), p.first.c_str());
+            if (!android::base::WriteStringToFile("0", tracefs_event_enable_path)) {
+                LOG(ERROR) << "Failed to disable tracing on: " << tracefs_event_enable_path;
                 if (p.second) {
                     ret = Status::ERROR_TRACING_POINT;
                 }
diff --git a/atrace/1.0/default/AtraceDevice.h b/atrace/1.0/default/AtraceDevice.h
index e700f89..ab87c65 100644
--- a/atrace/1.0/default/AtraceDevice.h
+++ b/atrace/1.0/default/AtraceDevice.h
@@ -36,12 +36,16 @@
 using ::android::hardware::Void;
 
 struct AtraceDevice : public IAtraceDevice {
+    AtraceDevice();
     // Methods from ::android::hardware::atrace::V1_0::IAtraceDevice follow.
     Return<void> listCategories(listCategories_cb _hidl_cb) override;
     Return<::android::hardware::atrace::V1_0::Status> enableCategories(
         const hidl_vec<hidl_string>& categories) override;
     Return<::android::hardware::atrace::V1_0::Status> disableAllCategories() override;
 
+  private:
+    std::string tracefs_event_root_;
+
     // Methods from ::android::hidl::base::V1_0::IBase follow.
 };
 
diff --git a/atrace/1.0/default/android.hardware.atrace@1.0-service.rc b/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
index eb54c39..7110b45 100644
--- a/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
+++ b/atrace/1.0/default/android.hardware.atrace@1.0-service.rc
@@ -1,10 +1,14 @@
 on late-init
     # vendor graphics trace points
     chmod 0666 /sys/kernel/debug/tracing/events/sde/enable
+    chmod 0666 /sys/kernel/tracing/events/sde/enable
     chmod 0666 /sys/kernel/debug/tracing/events/mdss/enable
+    chmod 0666 /sys/kernel/tracing/events/mdss/enable
     chmod 0666 /sys/kernel/debug/tracing/events/mali_systrace/enable
+    chmod 0666 /sys/kernel/tracing/events/mali_systrace/enable
     # ion allocation trace point
     chmod 0666 /sys/kernel/debug/tracing/events/kmem/ion_alloc_buffer_start/enable
+    chmod 0666 /sys/kernel/tracing/events/kmem/ion_alloc_buffer_start/enable
 
 service vendor.atrace-hal-1-0 /vendor/bin/hw/android.hardware.atrace@1.0-service
     interface android.hardware.atrace@1.0::IAtraceDevice default
diff --git a/atrace/1.0/vts/functional/Android.bp b/atrace/1.0/vts/functional/Android.bp
index 07d3f7f..9681aa2 100644
--- a/atrace/1.0/vts/functional/Android.bp
+++ b/atrace/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalAtraceV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp b/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
index 2eaa03e..d7a9e08 100644
--- a/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
+++ b/atrace/1.0/vts/functional/VtsHalAtraceV1_0TargetTest.cpp
@@ -93,6 +93,7 @@
     EXPECT_EQ(Status::SUCCESS, ret);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtraceHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, AtraceHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAtraceDevice::descriptor)),
diff --git a/audio/2.0/Android.bp b/audio/2.0/Android.bp
index 02f8b40..2689355 100644
--- a/audio/2.0/Android.bp
+++ b/audio/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/audio/2.0/config/Android.bp b/audio/2.0/config/Android.bp
index 65a32eb..3844b2b 100644
--- a/audio/2.0/config/Android.bp
+++ b/audio/2.0/config/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 filegroup {
     name: "audio_policy_configuration_V2_0",
     srcs: ["audio_policy_configuration.xsd"],
diff --git a/audio/4.0/Android.bp b/audio/4.0/Android.bp
index bc695c8..1a05d41 100644
--- a/audio/4.0/Android.bp
+++ b/audio/4.0/Android.bp
@@ -1,13 +1,19 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio@4.0",
     root: "android.hardware",
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/audio/4.0/config/Android.bp b/audio/4.0/config/Android.bp
index 6aac899..5b559b1 100644
--- a/audio/4.0/config/Android.bp
+++ b/audio/4.0/config/Android.bp
@@ -1,7 +1,15 @@
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_policy_configuration_V4_0",
     srcs: ["audio_policy_configuration.xsd"],
     package_name: "audio.policy.configuration.V4_0",
 }
-
diff --git a/audio/5.0/Android.bp b/audio/5.0/Android.bp
index 9b28497..525b0b9 100644
--- a/audio/5.0/Android.bp
+++ b/audio/5.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio@5.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/audio/5.0/config/Android.bp b/audio/5.0/config/Android.bp
index a3f1c33..95354bd 100644
--- a/audio/5.0/config/Android.bp
+++ b/audio/5.0/config/Android.bp
@@ -1,7 +1,15 @@
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_policy_configuration_V5_0",
     srcs: ["audio_policy_configuration.xsd"],
     package_name: "audio.policy.configuration.V5_0",
 }
-
diff --git a/audio/5.0/config/api/current.txt b/audio/5.0/config/api/current.txt
index a1d8e1e..dbb5d3b 100644
--- a/audio/5.0/config/api/current.txt
+++ b/audio/5.0/config/api/current.txt
@@ -133,6 +133,7 @@
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_LDAC;
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_LHDC;
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_LHDC_LL;
+    enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_MAT;
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_MAT_1_0;
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_MAT_2_0;
     enum_constant public static final audio.policy.configuration.V5_0.AudioFormat AUDIO_FORMAT_MAT_2_1;
@@ -198,7 +199,7 @@
   public static class DevicePorts.DevicePort {
     ctor public DevicePorts.DevicePort();
     method public String getAddress();
-    method public java.util.List<audio.policy.configuration.V5_0.AudioFormat> getEncodedFormats();
+    method public java.util.List<java.lang.String> getEncodedFormats();
     method public audio.policy.configuration.V5_0.Gains getGains();
     method public java.util.List<audio.policy.configuration.V5_0.Profile> getProfile();
     method public audio.policy.configuration.V5_0.Role getRole();
@@ -206,7 +207,7 @@
     method public String getType();
     method public boolean get_default();
     method public void setAddress(String);
-    method public void setEncodedFormats(java.util.List<audio.policy.configuration.V5_0.AudioFormat>);
+    method public void setEncodedFormats(java.util.List<java.lang.String>);
     method public void setGains(audio.policy.configuration.V5_0.Gains);
     method public void setRole(audio.policy.configuration.V5_0.Role);
     method public void setTagName(String);
@@ -379,10 +380,10 @@
 
   public static class SurroundFormats.Format {
     ctor public SurroundFormats.Format();
-    method public audio.policy.configuration.V5_0.AudioFormat getName();
-    method public java.util.List<audio.policy.configuration.V5_0.AudioFormat> getSubformats();
-    method public void setName(audio.policy.configuration.V5_0.AudioFormat);
-    method public void setSubformats(java.util.List<audio.policy.configuration.V5_0.AudioFormat>);
+    method public String getName();
+    method public java.util.List<java.lang.String> getSubformats();
+    method public void setName(String);
+    method public void setSubformats(java.util.List<java.lang.String>);
   }
 
   public class SurroundSound {
diff --git a/audio/5.0/config/audio_policy_configuration.xsd b/audio/5.0/config/audio_policy_configuration.xsd
index 284d2e2..f92136c 100644
--- a/audio/5.0/config/audio_policy_configuration.xsd
+++ b/audio/5.0/config/audio_policy_configuration.xsd
@@ -361,6 +361,7 @@
             <xs:enumeration value="AUDIO_FORMAT_AC4"/>
             <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
             <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
@@ -610,13 +611,13 @@
         </xs:sequence>
     </xs:complexType>
     <xs:simpleType name="audioFormatsList">
-        <xs:list itemType="audioFormat" />
+        <xs:list itemType="extendableAudioFormat" />
     </xs:simpleType>
     <xs:complexType name="surroundFormats">
         <xs:sequence>
             <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
-                    <xs:attribute name="name" type="audioFormat" use="required"/>
+                    <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
                     <xs:attribute name="subformats" type="audioFormatsList" />
                 </xs:complexType>
             </xs:element>
diff --git a/audio/6.0/Android.bp b/audio/6.0/Android.bp
index 16abc52..09705e6 100644
--- a/audio/6.0/Android.bp
+++ b/audio/6.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio@6.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/audio/6.0/config/Android.bp b/audio/6.0/config/Android.bp
index 182dfcc..c8ca19e 100644
--- a/audio/6.0/config/Android.bp
+++ b/audio/6.0/config/Android.bp
@@ -1,7 +1,15 @@
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_policy_configuration_V6_0",
     srcs: ["audio_policy_configuration.xsd"],
     package_name: "audio.policy.configuration.V6_0",
 }
-
diff --git a/audio/6.0/config/api/current.txt b/audio/6.0/config/api/current.txt
index 6b49e5e..01db90e 100644
--- a/audio/6.0/config/api/current.txt
+++ b/audio/6.0/config/api/current.txt
@@ -133,6 +133,7 @@
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_LDAC;
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_LHDC;
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_LHDC_LL;
+    enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_MAT;
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_MAT_1_0;
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_MAT_2_0;
     enum_constant public static final audio.policy.configuration.V6_0.AudioFormat AUDIO_FORMAT_MAT_2_1;
@@ -198,7 +199,7 @@
   public static class DevicePorts.DevicePort {
     ctor public DevicePorts.DevicePort();
     method public String getAddress();
-    method public java.util.List<audio.policy.configuration.V6_0.AudioFormat> getEncodedFormats();
+    method public java.util.List<java.lang.String> getEncodedFormats();
     method public audio.policy.configuration.V6_0.Gains getGains();
     method public java.util.List<audio.policy.configuration.V6_0.Profile> getProfile();
     method public audio.policy.configuration.V6_0.Role getRole();
@@ -206,7 +207,7 @@
     method public String getType();
     method public boolean get_default();
     method public void setAddress(String);
-    method public void setEncodedFormats(java.util.List<audio.policy.configuration.V6_0.AudioFormat>);
+    method public void setEncodedFormats(java.util.List<java.lang.String>);
     method public void setGains(audio.policy.configuration.V6_0.Gains);
     method public void setRole(audio.policy.configuration.V6_0.Role);
     method public void setTagName(String);
@@ -390,10 +391,10 @@
 
   public static class SurroundFormats.Format {
     ctor public SurroundFormats.Format();
-    method public audio.policy.configuration.V6_0.AudioFormat getName();
-    method public java.util.List<audio.policy.configuration.V6_0.AudioFormat> getSubformats();
-    method public void setName(audio.policy.configuration.V6_0.AudioFormat);
-    method public void setSubformats(java.util.List<audio.policy.configuration.V6_0.AudioFormat>);
+    method public String getName();
+    method public java.util.List<java.lang.String> getSubformats();
+    method public void setName(String);
+    method public void setSubformats(java.util.List<java.lang.String>);
   }
 
   public class SurroundSound {
diff --git a/audio/6.0/config/audio_policy_configuration.xsd b/audio/6.0/config/audio_policy_configuration.xsd
index 341c6b3..c2b8c5d 100644
--- a/audio/6.0/config/audio_policy_configuration.xsd
+++ b/audio/6.0/config/audio_policy_configuration.xsd
@@ -363,6 +363,7 @@
             <xs:enumeration value="AUDIO_FORMAT_AC4"/>
             <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
             <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
             <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
@@ -613,13 +614,13 @@
         </xs:sequence>
     </xs:complexType>
     <xs:simpleType name="audioFormatsList">
-        <xs:list itemType="audioFormat" />
+        <xs:list itemType="extendableAudioFormat" />
     </xs:simpleType>
     <xs:complexType name="surroundFormats">
         <xs:sequence>
             <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
                 <xs:complexType>
-                    <xs:attribute name="name" type="audioFormat" use="required"/>
+                    <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
                     <xs:attribute name="subformats" type="audioFormatsList" />
                 </xs:complexType>
             </xs:element>
diff --git a/audio/7.0/Android.bp b/audio/7.0/Android.bp
new file mode 100644
index 0000000..5533df3
--- /dev/null
+++ b/audio/7.0/Android.bp
@@ -0,0 +1,34 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.audio@7.0",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IDevice.hal",
+        "IDevicesFactory.hal",
+        "IPrimaryDevice.hal",
+        "IStream.hal",
+        "IStreamIn.hal",
+        "IStreamOut.hal",
+        "IStreamOutCallback.hal",
+        "IStreamOutEventCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.effect@7.0",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
+    ],
+    gen_java: false,
+    gen_java_constants: true,
+}
diff --git a/audio/7.0/IDevice.hal b/audio/7.0/IDevice.hal
new file mode 100644
index 0000000..85c789a
--- /dev/null
+++ b/audio/7.0/IDevice.hal
@@ -0,0 +1,368 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import IStreamIn;
+import IStreamOut;
+
+interface IDevice {
+    /**
+     * Returns whether the audio hardware interface has been initialized.
+     *
+     * @return retval OK on success, NOT_INITIALIZED on failure.
+     */
+    initCheck() generates (Result retval);
+
+    /**
+     * Sets the audio volume for all audio activities other than voice call. If
+     * NOT_SUPPORTED is returned, the software mixer will emulate this
+     * capability.
+     *
+     * @param volume 1.0f means unity, 0.0f is zero.
+     * @return retval operation completion status.
+     */
+    setMasterVolume(float volume) generates (Result retval);
+
+    /**
+     * Get the current master volume value for the HAL, if the HAL supports
+     * master volume control. For example, AudioFlinger will query this value
+     * from the primary audio HAL when the service starts and use the value for
+     * setting the initial master volume across all HALs. HALs which do not
+     * support this method must return NOT_SUPPORTED in 'retval'.
+     *
+     * @return retval operation completion status.
+     * @return volume 1.0f means unity, 0.0f is zero.
+     */
+    getMasterVolume() generates (Result retval, float volume);
+
+    /**
+     * Sets microphone muting state.
+     *
+     * @param mute whether microphone is muted.
+     * @return retval operation completion status.
+     */
+    setMicMute(bool mute) generates (Result retval);
+
+    /**
+     * Gets whether microphone is muted.
+     *
+     * @return retval operation completion status.
+     * @return mute whether microphone is muted.
+     */
+    getMicMute() generates (Result retval, bool mute);
+
+    /**
+     * Set the audio mute status for all audio activities. If the return value
+     * is NOT_SUPPORTED, the software mixer will emulate this capability.
+     *
+     * @param mute whether audio is muted.
+     * @return retval operation completion status.
+     */
+    setMasterMute(bool mute) generates (Result retval);
+
+    /**
+     * Get the current master mute status for the HAL, if the HAL supports
+     * master mute control. AudioFlinger will query this value from the primary
+     * audio HAL when the service starts and use the value for setting the
+     * initial master mute across all HALs. HAL must indicate that the feature
+     * is not supported by returning NOT_SUPPORTED status.
+     *
+     * @return retval operation completion status.
+     * @return mute whether audio is muted.
+     */
+    getMasterMute() generates (Result retval, bool mute);
+
+    /**
+     * Returns audio input buffer size according to parameters passed or
+     * INVALID_ARGUMENTS if one of the parameters is not supported.
+     *
+     * @param config audio configuration.
+     * @return retval operation completion status.
+     * @return bufferSize input buffer size in bytes.
+     */
+    getInputBufferSize(AudioConfig config)
+            generates (Result retval, uint64_t bufferSize);
+
+    /**
+     * This method creates and opens the audio hardware output stream.
+     * If the stream can not be opened with the proposed audio config,
+     * HAL must provide suggested values for the audio config.
+     *
+     * Note: INVALID_ARGUMENTS is returned both in the case when the
+     * HAL can not use the provided config and in the case when
+     * the value of any argument is invalid. In the latter case the
+     * HAL must provide a default initialized suggested config.
+     *
+     * @param ioHandle handle assigned by AudioFlinger.
+     * @param device device type and (if needed) address.
+     * @param config stream configuration.
+     * @param flags additional flags.
+     * @param sourceMetadata Description of the audio that will be played.
+                             May be used by implementations to configure hardware effects.
+     * @return retval operation completion status.
+     * @return outStream created output stream.
+     * @return suggestedConfig in the case of rejection of the proposed config,
+     *                         a config suggested by the HAL.
+     */
+    openOutputStream(
+            AudioIoHandle ioHandle,
+            DeviceAddress device,
+            AudioConfig config,
+            vec<AudioInOutFlag> flags,
+            SourceMetadata sourceMetadata) generates (
+                    Result retval,
+                    IStreamOut outStream,
+                    AudioConfig suggestedConfig);
+
+    /**
+     * This method creates and opens the audio hardware input stream.
+     * If the stream can not be opened with the proposed audio config,
+     * HAL must provide suggested values for the audio config.
+     *
+     * Note: INVALID_ARGUMENTS is returned both in the case when the
+     * HAL can not use the provided config and in the case when
+     * the value of any argument is invalid. In the latter case the
+     * HAL must provide a default initialized suggested config.
+     *
+     * @param ioHandle handle assigned by AudioFlinger.
+     * @param device device type and (if needed) address.
+     * @param config stream configuration.
+     * @param flags additional flags.
+     * @param sinkMetadata Description of the audio that is suggested by the client.
+     *                     May be used by implementations to configure processing effects.
+     * @return retval operation completion status.
+     * @return inStream in case of success, created input stream.
+     * @return suggestedConfig in the case of rejection of the proposed config,
+     *                         a config suggested by the HAL.
+     */
+    openInputStream(
+            AudioIoHandle ioHandle,
+            DeviceAddress device,
+            AudioConfig config,
+            vec<AudioInOutFlag> flags,
+            SinkMetadata sinkMetadata) generates (
+                    Result retval,
+                    IStreamIn inStream,
+                    AudioConfig suggestedConfig);
+
+    /**
+     * Returns whether HAL supports audio patches. Patch represents a connection
+     * between signal source(s) and signal sink(s). If HAL doesn't support
+     * patches natively (in hardware) then audio system will need to establish
+     * them in software.
+     *
+     * @return supports true if audio patches are supported.
+     */
+    supportsAudioPatches() generates (bool supports);
+
+    /**
+     * Creates an audio patch between several source and sink ports.  The handle
+     * is allocated by the HAL and must be unique for this audio HAL module.
+     *
+     * Optional method. HAL must support it if 'supportsAudioPatches' returns
+     * 'true'.
+     *
+     * @param sources patch sources.
+     * @param sinks patch sinks.
+     * @return retval operation completion status.
+     * @return patch created patch handle.
+     */
+    createAudioPatch(vec<AudioPortConfig> sources, vec<AudioPortConfig> sinks)
+            generates (Result retval, AudioPatchHandle patch);
+
+    /**
+     * Updates an audio patch.
+     *
+     * Use of this function is preferred to releasing and re-creating a patch
+     * as the HAL module can figure out a way of switching the route without
+     * causing audio disruption.
+     *
+     * Optional method. HAL must support it if 'supportsAudioPatches' returns
+     * 'true'.
+     *
+     * @param previousPatch handle of the previous patch to update.
+     * @param sources new patch sources.
+     * @param sinks new patch sinks.
+     * @return retval operation completion status.
+     * @return patch updated patch handle.
+     */
+    updateAudioPatch(
+            AudioPatchHandle previousPatch,
+            vec<AudioPortConfig> sources,
+            vec<AudioPortConfig> sinks) generates (
+                    Result retval, AudioPatchHandle patch);
+
+    /**
+     * Release an audio patch.
+     *
+     * Optional method. HAL must support it if 'supportsAudioPatches' returns
+     * 'true'.
+     *
+     * @param patch patch handle.
+     * @return retval operation completion status.
+     */
+    releaseAudioPatch(AudioPatchHandle patch) generates (Result retval);
+
+    /**
+     * Returns the list of supported attributes for a given audio port.
+     *
+     * As input, 'port' contains the information (type, role, address etc...)
+     * needed by the HAL to identify the port.
+     *
+     * As output, 'resultPort' contains possible attributes (sampling rates,
+     * formats, channel masks, gain controllers...) for this port.
+     *
+     * @param port port identifier.
+     * @return retval operation completion status.
+     * @return resultPort port descriptor with all parameters filled up.
+     */
+    getAudioPort(AudioPort port)
+            generates (Result retval, AudioPort resultPort);
+
+    /**
+     * Set audio port configuration.
+     *
+     * @param config audio port configuration.
+     * @return retval operation completion status.
+     */
+    setAudioPortConfig(AudioPortConfig config) generates (Result retval);
+
+    /**
+     * Gets the HW synchronization source of the device. Calling this method is
+     * equivalent to getting AUDIO_PARAMETER_HW_AV_SYNC on the legacy HAL.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status: OK or NOT_SUPPORTED.
+     * @return hwAvSync HW synchronization source
+     */
+    getHwAvSync() generates (Result retval, AudioHwSync hwAvSync);
+
+    /**
+     * Sets whether the screen is on. Calling this method is equivalent to
+     * setting AUDIO_PARAMETER_KEY_SCREEN_STATE on the legacy HAL.
+     *
+     * Optional method
+     *
+     * @param turnedOn whether the screen is turned on.
+     * @return retval operation completion status.
+     */
+    setScreenState(bool turnedOn) generates (Result retval);
+
+    /**
+     * Generic method for retrieving vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Multiple parameters can be retrieved at the same time.
+     * The implementation should return as many requested parameters
+     * as possible, even if one or more is not supported
+     *
+     * @param context provides more information about the request
+     * @param keys keys of the requested parameters
+     * @return retval operation completion status.
+     *         OK must be returned if keys is empty.
+     *         NOT_SUPPORTED must be returned if at least one key is unknown.
+     * @return parameters parameter key value pairs.
+     *         Must contain the value of all requested keys if retval == OK
+     */
+    getParameters(vec<ParameterValue> context, vec<string> keys)
+            generates (Result retval, vec<ParameterValue> parameters);
+
+    /**
+     * Generic method for setting vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Multiple parameters can be set at the same time though this is
+     * discouraged as it make failure analysis harder.
+     *
+     * If possible, a failed setParameters should not impact the platform state.
+     *
+     * @param context provides more information about the request
+     * @param parameters parameter key value pairs.
+     * @return retval operation completion status.
+     *         All parameters must be successfully set for OK to be returned
+     */
+    setParameters(vec<ParameterValue> context, vec<ParameterValue> parameters)
+            generates (Result retval);
+
+    /**
+     * Returns an array with available microphones in device.
+     *
+     * @return retval NOT_SUPPORTED if there are no microphones on this device
+     *                INVALID_STATE if the call is not successful,
+     *                OK otherwise.
+     *
+     * @return microphones array with microphones info
+     */
+    getMicrophones()
+         generates(Result retval, vec<MicrophoneInfo> microphones);
+
+    /**
+     * Notifies the device module about the connection state of an input/output
+     * device attached to it. Calling this method is equivalent to setting
+     * AUDIO_PARAMETER_DEVICE_[DIS]CONNECT on the legacy HAL.
+     *
+     * @param address audio device specification.
+     * @param connected whether the device is connected.
+     * @return retval operation completion status.
+     */
+    setConnectedState(DeviceAddress address, bool connected)
+            generates (Result retval);
+
+    /**
+     * Called by the framework to deinitialize the device and free up
+     * all currently allocated resources. It is recommended to close
+     * the device on the client side as soon as it is becomes unused.
+     *
+     * Note that all streams must be closed by the client before
+     * attempting to close the device they belong to.
+     *
+     * @return retval OK in case the success.
+     *                INVALID_STATE if the device was already closed
+     *                or there are streams currently opened.
+     */
+    close() generates (Result retval);
+
+    /**
+     * Applies an audio effect to an audio device. The effect is inserted
+     * according to its insertion preference specified by INSERT_... EffectFlags
+     * in the EffectDescriptor.
+     *
+     * @param device identifies the sink or source device this effect must be applied to.
+     *               "device" is the AudioPortHandle indicated for the device when the audio
+     *                patch connecting that device was created.
+     * @param effectId effect ID (obtained from IEffectsFactory.createEffect) of
+     *                 the effect to add.
+     * @return retval operation completion status.
+     */
+    addDeviceEffect(AudioPortHandle device, uint64_t effectId) generates (Result retval);
+
+    /**
+     * Stops applying an audio effect to an audio device.
+     *
+     * @param device identifies the sink or source device this effect was applied to.
+     *               "device" is the AudioPortHandle indicated for the device when the audio
+     *               patch is created at the audio HAL.
+     * @param effectId effect ID (obtained from IEffectsFactory.createEffect) of
+     *                 the effect.
+     * @return retval operation completion status.
+     */
+    removeDeviceEffect(AudioPortHandle device, uint64_t effectId) generates (Result retval);
+};
diff --git a/audio/7.0/IDevicesFactory.hal b/audio/7.0/IDevicesFactory.hal
new file mode 100644
index 0000000..03549b4
--- /dev/null
+++ b/audio/7.0/IDevicesFactory.hal
@@ -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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import IDevice;
+import IPrimaryDevice;
+
+/** This factory allows a HAL implementation to be split in multiple independent
+ *  devices (called module in the pre-treble API).
+ *  Note that this division is arbitrary and implementation are free
+ *  to only have a Primary.
+ *  The framework will query the devices according to audio_policy_configuration.xml
+ *
+ *  Each device name is arbitrary, provided by the vendor's audio_policy_configuration.xml
+ *  and only used to identify a device in this factory.
+ *  The framework must not interpret the name, treating it as a vendor opaque data
+ *  with the following exception:
+ *  - the "r_submix" device that must be present to support policyMixes (Eg: Android projected).
+ *    Note that this Device is included by default in a build derived from AOSP.
+ *
+ *  Note that on AOSP Oreo (including MR1) the "a2dp" module is not using this API
+ *  but is loaded directly from the system partition using the legacy API
+ *  due to limitations with the Bluetooth framework.
+ */
+interface IDevicesFactory {
+
+    /**
+     * Opens an audio device. To close the device, it is necessary to release
+     * references to the returned device object.
+     *
+     * @param device device name.
+     * @return retval operation completion status. Returns INVALID_ARGUMENTS
+     *         if there is no corresponding hardware module found,
+     *         NOT_INITIALIZED if an error occurred while opening the hardware
+     *         module.
+     * @return result the interface for the created device.
+     */
+    openDevice(string device) generates (Result retval, IDevice result);
+
+    /**
+     * Opens the Primary audio device that must be present.
+     * This function is not optional and must return successfully the primary device.
+     *
+     * This device must have the name "primary".
+     *
+     * The telephony stack uses this device to control the audio during a voice call.
+     *
+     * @return retval operation completion status. Must be SUCCESS.
+     *         For debugging, return INVALID_ARGUMENTS if there is no corresponding
+     *         hardware module found, NOT_INITIALIZED if an error occurred
+     *         while opening the hardware module.
+     * @return result the interface for the created device.
+     */
+    openPrimaryDevice() generates (Result retval, IPrimaryDevice result);
+};
diff --git a/audio/7.0/IPrimaryDevice.hal b/audio/7.0/IPrimaryDevice.hal
new file mode 100644
index 0000000..1427ae8
--- /dev/null
+++ b/audio/7.0/IPrimaryDevice.hal
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import IDevice;
+
+interface IPrimaryDevice extends IDevice {
+    /**
+     * Sets the audio volume of a voice call.
+     *
+     * @param volume 1.0f means unity, 0.0f is zero.
+     * @return retval operation completion status.
+     */
+    setVoiceVolume(float volume) generates (Result retval);
+
+    /**
+     * This method is used to notify the HAL about audio mode changes.
+     *
+     * @param mode new mode.
+     * @return retval operation completion status.
+     */
+    setMode(AudioMode mode) generates (Result retval);
+
+    /**
+     * Sets the name of the current BT SCO headset. Calling this method
+     * is equivalent to setting legacy "bt_headset_name" parameter.
+     * The BT SCO headset name must only be used for debugging purposes.
+     * Optional method
+     *
+     * @param name the name of the current BT SCO headset (can be empty).
+     * @return retval operation completion status.
+     */
+    setBtScoHeadsetDebugName(string name) generates (Result retval);
+
+    /**
+     * Gets whether BT SCO Noise Reduction and Echo Cancellation are enabled.
+     * Calling this method is equivalent to getting AUDIO_PARAMETER_KEY_BT_NREC
+     * on the legacy HAL.
+     *
+     * @return retval operation completion status.
+     * @return enabled whether BT SCO NR + EC are enabled.
+     */
+    getBtScoNrecEnabled() generates (Result retval, bool enabled);
+
+    /**
+     * Sets whether BT SCO Noise Reduction and Echo Cancellation are enabled.
+     * Calling this method is equivalent to setting AUDIO_PARAMETER_KEY_BT_NREC
+     * on the legacy HAL.
+     * Optional method
+     *
+     * @param enabled whether BT SCO NR + EC are enabled.
+     * @return retval operation completion status.
+     */
+    setBtScoNrecEnabled(bool enabled) generates (Result retval);
+
+    /**
+     * Gets whether BT SCO Wideband mode is enabled. Calling this method is
+     * equivalent to getting AUDIO_PARAMETER_KEY_BT_SCO_WB on the legacy HAL.
+     *
+     * @return retval operation completion status.
+     * @return enabled whether BT Wideband is enabled.
+     */
+    getBtScoWidebandEnabled() generates (Result retval, bool enabled);
+
+    /**
+     * Sets whether BT SCO Wideband mode is enabled. Calling this method is
+     * equivalent to setting AUDIO_PARAMETER_KEY_BT_SCO_WB on the legacy HAL.
+     * Optional method
+     *
+     * @param enabled whether BT Wideband is enabled.
+     * @return retval operation completion status.
+     */
+    setBtScoWidebandEnabled(bool enabled) generates (Result retval);
+
+    /**
+     * Gets whether BT HFP (Hands-Free Profile) is enabled. Calling this method
+     * is equivalent to getting "hfp_enable" parameter value on the legacy HAL.
+     *
+     * @return retval operation completion status.
+     * @return enabled whether BT HFP is enabled.
+     */
+    getBtHfpEnabled() generates (Result retval, bool enabled);
+
+    /**
+     * Sets whether BT HFP (Hands-Free Profile) is enabled. Calling this method
+     * is equivalent to setting "hfp_enable" parameter on the legacy HAL.
+     * Optional method
+     *
+     * @param enabled whether BT HFP is enabled.
+     * @return retval operation completion status.
+     */
+    setBtHfpEnabled(bool enabled) generates (Result retval);
+
+    /**
+     * Sets the sampling rate of BT HFP (Hands-Free Profile). Calling this
+     * method is equivalent to setting "hfp_set_sampling_rate" parameter
+     * on the legacy HAL.
+     * Optional method
+     *
+     * @param sampleRateHz sample rate in Hz.
+     * @return retval operation completion status.
+     */
+    setBtHfpSampleRate(uint32_t sampleRateHz) generates (Result retval);
+
+    /**
+     * Sets the current output volume Hz for BT HFP (Hands-Free Profile).
+     * Calling this method is equivalent to setting "hfp_volume" parameter value
+     * on the legacy HAL (except that legacy HAL implementations expect
+     * an integer value in the range from 0 to 15.)
+     * Optional method
+     *
+     * @param volume 1.0f means unity, 0.0f is zero.
+     * @return retval operation completion status.
+     */
+    setBtHfpVolume(float volume) generates (Result retval);
+
+    enum TtyMode : int32_t {
+        OFF,
+        VCO,
+        HCO,
+        FULL
+    };
+
+    /**
+     * Gets current TTY mode selection. Calling this method is equivalent to
+     * getting AUDIO_PARAMETER_KEY_TTY_MODE on the legacy HAL.
+     *
+     * @return retval operation completion status.
+     * @return mode TTY mode.
+     */
+    getTtyMode() generates (Result retval, TtyMode mode);
+
+    /**
+     * Sets current TTY mode. Calling this method is equivalent to setting
+     * AUDIO_PARAMETER_KEY_TTY_MODE on the legacy HAL.
+     *
+     * @param mode TTY mode.
+     * @return retval operation completion status.
+     */
+    setTtyMode(TtyMode mode) generates (Result retval);
+
+    /**
+     * Gets whether Hearing Aid Compatibility - Telecoil (HAC-T) mode is
+     * enabled. Calling this method is equivalent to getting
+     * AUDIO_PARAMETER_KEY_HAC on the legacy HAL.
+     *
+     * @return retval operation completion status.
+     * @return enabled whether HAC mode is enabled.
+     */
+    getHacEnabled() generates (Result retval, bool enabled);
+
+    /**
+     * Sets whether Hearing Aid Compatibility - Telecoil (HAC-T) mode is
+     * enabled. Calling this method is equivalent to setting
+     * AUDIO_PARAMETER_KEY_HAC on the legacy HAL.
+     * Optional method
+     *
+     * @param enabled whether HAC mode is enabled.
+     * @return retval operation completion status.
+     */
+    setHacEnabled(bool enabled) generates (Result retval);
+
+    enum Rotation : int32_t {
+        DEG_0,
+        DEG_90,
+        DEG_180,
+        DEG_270
+    };
+
+    /**
+     * Updates HAL on the current rotation of the device relative to natural
+     * orientation. Calling this method is equivalent to setting legacy
+     * parameter "rotation".
+     *
+     * @param rotation rotation in degrees relative to natural device
+     *     orientation.
+     * @return retval operation completion status.
+     */
+    updateRotation(Rotation rotation) generates (Result retval);
+};
diff --git a/audio/7.0/IStream.hal b/audio/7.0/IStream.hal
new file mode 100644
index 0000000..e4987c2
--- /dev/null
+++ b/audio/7.0/IStream.hal
@@ -0,0 +1,250 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import android.hardware.audio.effect@7.0::IEffect;
+
+interface IStream {
+    /**
+     * Return the frame size (number of bytes per sample).
+     *
+     * @return frameSize frame size in bytes.
+     */
+    getFrameSize() generates (uint64_t frameSize);
+
+    /**
+     * Return the frame count of the buffer. Calling this method is equivalent
+     * to getting AUDIO_PARAMETER_STREAM_FRAME_COUNT on the legacy HAL.
+     *
+     * @return count frame count.
+     */
+    getFrameCount() generates (uint64_t count);
+
+    /**
+     * Return the size of input/output buffer in bytes for this stream.
+     * It must be a multiple of the frame size.
+     *
+     * @return buffer buffer size in bytes.
+     */
+    getBufferSize() generates (uint64_t bufferSize);
+
+    /**
+     * Return supported audio profiles for this particular stream. This method
+     * is normally called for streams opened on devices that use dynamic
+     * profiles, e.g. HDMI and USB interfaces. Please note that supported
+     * profiles of the stream may differ from the capabilities of the connected
+     * physical device.
+     *
+     * For devices with fixed configurations, e.g. built-in audio devices, all
+     * the profiles are specified in the audio_policy_configuration.xml
+     * file. For such devices, this method must return the configuration from
+     * the config file, or NOT_SUPPORTED retval.
+     *
+     * @return retval operation completion status.
+     * @return formats supported audio profiles.
+     *                 Must be non empty if retval is OK.
+     */
+    getSupportedProfiles()
+            generates (Result retval, vec<AudioProfile> profiles);
+
+    /**
+     * Retrieves basic stream configuration: sample rate, audio format,
+     * channel mask.
+     *
+     * @return retval operation completion status.
+     * @return config basic stream configuration.
+     */
+    getAudioProperties() generates (Result retval, AudioConfigBase config);
+
+    /**
+     * Sets stream parameters. Only sets parameters that are specified.
+     *
+     * Optional method. If implemented, only called on a stopped stream.
+     *
+     * @param config basic stream configuration.
+     * @return retval operation completion status.
+     */
+    setAudioProperties(AudioConfigBaseOptional config)
+            generates (Result retval);
+
+    /**
+     * Applies audio effect to the stream.
+     *
+     * @param effectId effect ID (obtained from IEffectsFactory.createEffect) of
+     *                 the effect to apply.
+     * @return retval operation completion status.
+     */
+    addEffect(uint64_t effectId) generates (Result retval);
+
+    /**
+     * Stops application of the effect to the stream.
+     *
+     * @param effectId effect ID (obtained from IEffectsFactory.createEffect) of
+     *                 the effect to remove.
+     * @return retval operation completion status.
+     */
+    removeEffect(uint64_t effectId) generates (Result retval);
+
+    /**
+     * Put the audio hardware input/output into standby mode.
+     * Driver must exit from standby mode at the next I/O operation.
+     *
+     * @return retval operation completion status.
+     */
+    standby() generates (Result retval);
+
+    /**
+     * Return the set of devices which this stream is connected to.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status: OK or NOT_SUPPORTED.
+     * @return device set of devices which this stream is connected to.
+     */
+    getDevices() generates (Result retval, vec<DeviceAddress> devices);
+
+    /**
+     * Connects the stream to one or multiple devices.
+     *
+     * This method must only be used for HALs that do not support
+     * 'IDevice.createAudioPatch' method. Calling this method is
+     * equivalent to setting AUDIO_PARAMETER_STREAM_ROUTING preceded
+     * with a device address in the legacy HAL interface.
+     *
+     * @param address device to connect the stream to.
+     * @return retval operation completion status.
+     */
+    setDevices(vec<DeviceAddress> devices) generates (Result retval);
+
+    /**
+     * Sets the HW synchronization source. Calling this method is equivalent to
+     * setting AUDIO_PARAMETER_STREAM_HW_AV_SYNC on the legacy HAL.
+     *
+     * Optional method
+     *
+     * @param hwAvSync HW synchronization source
+     * @return retval operation completion status.
+     */
+    setHwAvSync(AudioHwSync hwAvSync) generates (Result retval);
+
+    /**
+     * Generic method for retrieving vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Multiple parameters can be retrieved at the same time.
+     * The implementation should return as many requested parameters
+     * as possible, even if one or more is not supported
+     *
+     * @param context provides more information about the request
+     * @param keys keys of the requested parameters
+     * @return retval operation completion status.
+     *         OK must be returned if keys is empty.
+     *         NOT_SUPPORTED must be returned if at least one key is unknown.
+     * @return parameters parameter key value pairs.
+     *         Must contain the value of all requested keys if retval == OK
+     */
+    getParameters(vec<ParameterValue> context, vec<string> keys)
+            generates (Result retval, vec<ParameterValue> parameters);
+
+    /**
+     * Generic method for setting vendor-specific parameter values.
+     * The framework does not interpret the parameters, they are passed
+     * in an opaque manner between a vendor application and HAL.
+     *
+     * Multiple parameters can be set at the same time though this is
+     * discouraged as it make failure analysis harder.
+     *
+     * If possible, a failed setParameters should not impact the platform state.
+     *
+     * @param context provides more information about the request
+     * @param parameters parameter key value pairs.
+     * @return retval operation completion status.
+     *         All parameters must be successfully set for OK to be returned
+     */
+    setParameters(vec<ParameterValue> context, vec<ParameterValue> parameters)
+            generates (Result retval);
+
+    /**
+     * Called by the framework to start a stream operating in mmap mode.
+     * createMmapBuffer() must be called before calling start().
+     * Function only implemented by streams operating in mmap mode.
+     *
+     * @return retval OK in case the success.
+     *                NOT_SUPPORTED on non mmap mode streams
+     *                INVALID_STATE if called out of sequence
+     */
+    start() generates (Result retval);
+
+    /**
+     * Called by the framework to stop a stream operating in mmap mode.
+     * Function only implemented by streams operating in mmap mode.
+     *
+     * @return retval OK in case the success.
+     *                NOT_SUPPORTED on non mmap mode streams
+     *                INVALID_STATE if called out of sequence
+     */
+    stop() generates (Result retval) ;
+
+    /**
+     * Called by the framework to retrieve information on the mmap buffer used for audio
+     * samples transfer.
+     * Function only implemented by streams operating in mmap mode.
+     *
+     * @param minSizeFrames minimum buffer size requested. The actual buffer
+     *                     size returned in struct MmapBufferInfo can be larger.
+     *                     The size must be a positive value.
+     * @return retval OK in case the success.
+     *                NOT_SUPPORTED on non mmap mode streams
+     *                NOT_INITIALIZED in case of memory allocation error
+     *                INVALID_ARGUMENTS if the requested buffer size is invalid
+     *                INVALID_STATE if called out of sequence
+     * @return info    a MmapBufferInfo struct containing information on the MMMAP buffer created.
+     */
+    createMmapBuffer(int32_t minSizeFrames)
+            generates (Result retval, MmapBufferInfo info);
+
+    /**
+     * Called by the framework to read current read/write position in the mmap buffer
+     * with associated time stamp.
+     * Function only implemented by streams operating in mmap mode.
+     *
+     * @return retval OK in case the success.
+     *                NOT_SUPPORTED on non mmap mode streams
+     *                INVALID_STATE if called out of sequence
+     * @return position a MmapPosition struct containing current HW read/write position in frames
+     *                  with associated time stamp.
+     */
+    getMmapPosition()
+            generates (Result retval, MmapPosition position);
+
+    /**
+     * Called by the framework to deinitialize the stream and free up
+     * all currently allocated resources. It is recommended to close
+     * the stream on the client side as soon as it is becomes unused.
+     *
+     * The client must ensure that this function is not called while
+     * audio data is being transferred through the stream's message queues.
+     *
+     * @return retval OK in case the success.
+     *                NOT_SUPPORTED if called on IStream instead of input or
+     *                              output stream interface.
+     *                INVALID_STATE if the stream was already closed.
+     */
+    close() generates (Result retval);
+};
diff --git a/audio/7.0/IStreamIn.hal b/audio/7.0/IStreamIn.hal
new file mode 100644
index 0000000..be4bda4
--- /dev/null
+++ b/audio/7.0/IStreamIn.hal
@@ -0,0 +1,211 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import IStream;
+
+interface IStreamIn extends IStream {
+    /**
+     * Returns the source descriptor of the input stream. Calling this method is
+     * equivalent to getting AUDIO_PARAMETER_STREAM_INPUT_SOURCE on the legacy
+     * HAL.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return source audio source.
+     */
+    getAudioSource() generates (Result retval, AudioSource source);
+
+    /**
+     * Set the input gain for the audio driver.
+     *
+     * Optional method
+     *
+     * @param gain 1.0f is unity, 0.0f is zero.
+     * @result retval operation completion status.
+     */
+    setGain(float gain) generates (Result retval);
+
+    /**
+     * Called when the metadata of the stream's sink has been changed.
+     *
+     * Optional method
+     *
+     * @param sinkMetadata Description of the audio that is suggested by the clients.
+     * @return retval operation completion status.
+     *        If any of the metadata fields contains an invalid value,
+     *        returns INVALID_ARGUMENTS.
+     *        If method isn't supported by the HAL returns NOT_SUPPORTED.
+     */
+    updateSinkMetadata(SinkMetadata sinkMetadata) generates (Result retval);
+
+    /**
+     * Commands that can be executed on the driver reader thread.
+     */
+    enum ReadCommand : int32_t {
+        READ,
+        GET_CAPTURE_POSITION
+    };
+
+    /**
+     * Data structure passed to the driver for executing commands
+     * on the driver reader thread.
+     */
+    struct ReadParameters {
+        ReadCommand command;  // discriminator
+        union Params {
+            uint64_t read;    // READ command, amount of bytes to read, >= 0.
+            // No parameters for GET_CAPTURE_POSITION.
+        } params;
+    };
+
+    /**
+     * Data structure passed back to the client via status message queue
+     * of 'read' operation.
+     *
+     * Possible values of 'retval' field:
+     *  - OK, read operation was successful;
+     *  - INVALID_ARGUMENTS, stream was not configured properly;
+     *  - INVALID_STATE, stream is in a state that doesn't allow reads.
+     */
+    struct ReadStatus {
+        Result retval;
+        ReadCommand replyTo;  // discriminator
+        union Reply {
+            uint64_t read;    // READ command, amount of bytes read, >= 0.
+            struct CapturePosition { // same as generated by getCapturePosition.
+                uint64_t frames;
+                uint64_t time;
+            } capturePosition;
+        } reply;
+    };
+
+    /**
+     * Set up required transports for receiving audio buffers from the driver.
+     *
+     * The transport consists of three message queues:
+     *  -- command queue is used to instruct the reader thread what operation
+     *     to perform;
+     *  -- data queue is used for passing audio data from the driver
+     *     to the client;
+     *  -- status queue is used for reporting operation status
+     *     (e.g. amount of bytes actually read or error code).
+     *
+     * The driver operates on a dedicated thread. The client must ensure that
+     * the thread is given an appropriate priority and assigned to correct
+     * scheduler and cgroup. For this purpose, the method returns the identifier
+     * of the driver thread.
+     *
+     * @param frameSize the size of a single frame, in bytes.
+     * @param framesCount the number of frames in a buffer.
+     * @param threadPriority priority of the driver thread.
+     * @return retval OK if both message queues were created successfully.
+     *                INVALID_STATE if the method was already called.
+     *                INVALID_ARGUMENTS if there was a problem setting up
+     *                                  the queues.
+     * @return commandMQ a message queue used for passing commands.
+     * @return dataMQ a message queue used for passing audio data in the format
+     *                specified at the stream opening.
+     * @return statusMQ a message queue used for passing status from the driver
+     *                  using ReadStatus structures.
+     * @return threadId identifier of the driver's dedicated thread; the caller
+     *                  may adjust the thread priority to match the priority
+     *                  of the thread that provides audio data.
+     */
+    prepareForReading(uint32_t frameSize, uint32_t framesCount)
+    generates (
+            Result retval,
+            fmq_sync<ReadParameters> commandMQ,
+            fmq_sync<uint8_t> dataMQ,
+            fmq_sync<ReadStatus> statusMQ,
+            int32_t threadId);
+
+    /**
+     * Return the amount of input frames lost in the audio driver since the last
+     * call of this function.
+     *
+     * Audio driver is expected to reset the value to 0 and restart counting
+     * upon returning the current value by this function call. Such loss
+     * typically occurs when the user space process is blocked longer than the
+     * capacity of audio driver buffers.
+     *
+     * @return framesLost the number of input audio frames lost.
+     */
+    getInputFramesLost() generates (uint32_t framesLost);
+
+    /**
+     * Return a recent count of the number of audio frames received and the
+     * clock time associated with that frame count. The count must not reset to
+     * zero when a PCM input enters standby.
+     *
+     * @return retval INVALID_STATE if the device is not ready/available,
+     *                NOT_SUPPORTED if the command is not supported,
+     *                OK otherwise.
+     * @return frames the total frame count received. This must be as early in
+     *                the capture pipeline as possible. In general, frames
+     *                must be non-negative and must not go "backwards".
+     * @return time is the clock monotonic time when frames was measured. In
+     *              general, time must be a positive quantity and must not
+     *              go "backwards".
+     */
+    getCapturePosition()
+            generates (Result retval, uint64_t frames, uint64_t time);
+
+    /**
+     * Returns an array with active microphones in the stream.
+     *
+     * @return retval INVALID_STATE if the call is not successful,
+     *                OK otherwise.
+     *
+     * @return microphones array with microphones info
+     */
+    getActiveMicrophones()
+               generates(Result retval, vec<MicrophoneInfo> microphones);
+
+    /**
+     * Specifies the logical microphone (for processing).
+     *
+     * If the feature is not supported an error should be returned
+     * If multiple microphones are present, this should be treated as a preference
+     * for their combined direction.
+     *
+     * Optional method
+     *
+     * @param Direction constant
+     * @return retval OK if the call is successful, an error code otherwise.
+     */
+    setMicrophoneDirection(MicrophoneDirection direction)
+               generates(Result retval);
+
+    /**
+     * Specifies the zoom factor for the selected microphone (for processing).
+     *
+     * If the feature is not supported an error should be returned
+     * If multiple microphones are present, this should be treated as a preference
+     * for their combined field dimension.
+     *
+     * Optional method
+     *
+     * @param the desired field dimension of microphone capture. Range is from -1 (wide angle),
+     * though 0 (no zoom) to 1 (maximum zoom).
+     *
+     * @return retval OK if the call is not successful, an error code otherwise.
+     */
+    setMicrophoneFieldDimension(float zoom) generates(Result retval);
+};
diff --git a/audio/7.0/IStreamOut.hal b/audio/7.0/IStreamOut.hal
new file mode 100644
index 0000000..359dce4
--- /dev/null
+++ b/audio/7.0/IStreamOut.hal
@@ -0,0 +1,396 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+import IStream;
+import IStreamOutCallback;
+import IStreamOutEventCallback;
+
+interface IStreamOut extends IStream {
+    /**
+     * Return the audio hardware driver estimated latency in milliseconds.
+     *
+     * @return latencyMs latency in milliseconds.
+     */
+    getLatency() generates (uint32_t latencyMs);
+
+    /**
+     * This method is used in situations where audio mixing is done in the
+     * hardware. This method serves as a direct interface with hardware,
+     * allowing to directly set the volume as apposed to via the framework.
+     * This method might produce multiple PCM outputs or hardware accelerated
+     * codecs, such as MP3 or AAC.
+     *
+     * Optional method
+     *
+     * @param left left channel attenuation, 1.0f is unity, 0.0f is zero.
+     * @param right right channel attenuation, 1.0f is unity, 0.0f is zero.
+     * @return retval operation completion status.
+     *        If a volume is outside [0,1], return INVALID_ARGUMENTS
+     */
+    setVolume(float left, float right) generates (Result retval);
+
+    /**
+     * Called when the metadata of the stream's source has been changed.
+     *
+     * Optional method
+     *
+     * @param sourceMetadata Description of the audio that is played by the clients.
+     * @return retval operation completion status.
+     *        If any of the metadata fields contains an invalid value,
+     *        returns INVALID_ARGUMENTS.
+     *        If method isn't supported by the HAL returns NOT_SUPPORTED.
+     */
+    updateSourceMetadata(SourceMetadata sourceMetadata) generates (Result retval);
+
+    /**
+     * Commands that can be executed on the driver writer thread.
+     */
+    enum WriteCommand : int32_t {
+        WRITE,
+        GET_PRESENTATION_POSITION,
+        GET_LATENCY
+    };
+
+    /**
+     * Data structure passed back to the client via status message queue
+     * of 'write' operation.
+     *
+     * Possible values of 'retval' field:
+     *  - OK, write operation was successful;
+     *  - INVALID_ARGUMENTS, stream was not configured properly;
+     *  - INVALID_STATE, stream is in a state that doesn't allow writes;
+     *  - INVALID_OPERATION, retrieving presentation position isn't supported.
+     */
+    struct WriteStatus {
+        Result retval;
+        WriteCommand replyTo;  // discriminator
+        union Reply {
+            uint64_t written;  // WRITE command, amount of bytes written, >= 0.
+            struct PresentationPosition {  // same as generated by
+                uint64_t frames;           // getPresentationPosition.
+                TimeSpec timeStamp;
+            } presentationPosition;
+            uint32_t latencyMs; // Same as generated by getLatency.
+        } reply;
+    };
+
+    /**
+     * Set up required transports for passing audio buffers to the driver.
+     *
+     * The transport consists of three message queues:
+     *  -- command queue is used to instruct the writer thread what operation
+     *     to perform;
+     *  -- data queue is used for passing audio data from the client
+     *     to the driver;
+     *  -- status queue is used for reporting operation status
+     *     (e.g. amount of bytes actually written or error code).
+     *
+     * The driver operates on a dedicated thread. The client must ensure that
+     * the thread is given an appropriate priority and assigned to correct
+     * scheduler and cgroup. For this purpose, the method returns the identifier
+     * of the driver thread.
+     *
+     * @param frameSize the size of a single frame, in bytes.
+     * @param framesCount the number of frames in a buffer.
+     * @return retval OK if both message queues were created successfully.
+     *                INVALID_STATE if the method was already called.
+     *                INVALID_ARGUMENTS if there was a problem setting up
+     *                                  the queues.
+     * @return commandMQ a message queue used for passing commands.
+     * @return dataMQ a message queue used for passing audio data in the format
+     *                specified at the stream opening.
+     * @return statusMQ a message queue used for passing status from the driver
+     *                  using WriteStatus structures.
+     * @return threadId identifier of the driver's dedicated thread; the caller
+     *                  may adjust the thread priority to match the priority
+     *                  of the thread that provides audio data.
+     */
+    prepareForWriting(uint32_t frameSize, uint32_t framesCount)
+    generates (
+            Result retval,
+            fmq_sync<WriteCommand> commandMQ,
+            fmq_sync<uint8_t> dataMQ,
+            fmq_sync<WriteStatus> statusMQ,
+            int32_t threadId);
+
+    /**
+     * Return the number of audio frames written by the audio DSP to DAC since
+     * the output has exited standby.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return dspFrames number of audio frames written.
+     */
+    getRenderPosition() generates (Result retval, uint32_t dspFrames);
+
+    /**
+     * Get the local time at which the next write to the audio driver will be
+     * presented. The units are microseconds, where the epoch is decided by the
+     * local audio HAL.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return timestampUs time of the next write.
+     */
+    getNextWriteTimestamp() generates (Result retval, int64_t timestampUs);
+
+    /**
+     * Set the callback interface for notifying completion of non-blocking
+     * write and drain.
+     *
+     * Calling this function implies that all future 'write' and 'drain'
+     * must be non-blocking and use the callback to signal completion.
+     *
+     * 'clearCallback' method needs to be called in order to release the local
+     * callback proxy on the server side and thus dereference the callback
+     * implementation on the client side.
+     *
+     * @return retval operation completion status.
+     */
+    setCallback(IStreamOutCallback callback) generates (Result retval);
+
+    /**
+     * Clears the callback previously set via 'setCallback' method.
+     *
+     * Warning: failure to call this method results in callback implementation
+     * on the client side being held until the HAL server termination.
+     *
+     * If no callback was previously set, the method should be a no-op
+     * and return OK.
+     *
+     * @return retval operation completion status: OK or NOT_SUPPORTED.
+     */
+    clearCallback() generates (Result retval);
+
+    /**
+     * Set the callback interface for notifying about an output stream event.
+     *
+     * Calling this method with a null pointer will result in releasing
+     * the local callback proxy on the server side and thus dereference
+     * the callback implementation on the client side.
+     *
+     * @return retval operation completion status.
+     */
+    setEventCallback(IStreamOutEventCallback callback)
+            generates (Result retval);
+
+    /**
+     * Returns whether HAL supports pausing and resuming of streams.
+     *
+     * @return supportsPause true if pausing is supported.
+     * @return supportsResume true if resume is supported.
+     */
+    supportsPauseAndResume()
+            generates (bool supportsPause, bool supportsResume);
+
+    /**
+     * Notifies to the audio driver to stop playback however the queued buffers
+     * are retained by the hardware. Useful for implementing pause/resume. Empty
+     * implementation if not supported however must be implemented for hardware
+     * with non-trivial latency. In the pause state, some audio hardware may
+     * still be using power. Client code may consider calling 'suspend' after a
+     * timeout to prevent that excess power usage.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     *
+     * @return retval operation completion status.
+     */
+    pause() generates (Result retval);
+
+    /**
+     * Notifies to the audio driver to resume playback following a pause.
+     * Returns error INVALID_STATE if called without matching pause.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     *
+     * @return retval operation completion status.
+     */
+    resume() generates (Result retval);
+
+    /**
+     * Returns whether HAL supports draining of streams.
+     *
+     * @return supports true if draining is supported.
+     */
+    supportsDrain() generates (bool supports);
+
+    /**
+     * Requests notification when data buffered by the driver/hardware has been
+     * played. If 'setCallback' has previously been called to enable
+     * non-blocking mode, then 'drain' must not block, instead it must return
+     * quickly and completion of the drain is notified through the callback. If
+     * 'setCallback' has not been called, then 'drain' must block until
+     * completion.
+     *
+     * If 'type' is 'ALL', the drain completes when all previously written data
+     * has been played.
+     *
+     * If 'type' is 'EARLY_NOTIFY', the drain completes shortly before all data
+     * for the current track has played to allow time for the framework to
+     * perform a gapless track switch.
+     *
+     * Drain must return immediately on 'stop' and 'flush' calls.
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     *
+     * @param type type of drain.
+     * @return retval operation completion status.
+     */
+    drain(AudioDrain type) generates (Result retval);
+
+    /**
+     * Notifies to the audio driver to flush (that is, drop) the queued
+     * data. Stream must already be paused before calling 'flush'. For
+     * compressed and offload streams the frame count returned by
+     * 'getPresentationPosition' must reset after flush.
+     *
+     * Optional method
+     *
+     * Implementation of this function is mandatory for offloaded playback.
+     *
+     * @return retval operation completion status.
+     */
+    flush() generates (Result retval);
+
+    /**
+     * Return a recent count of the number of audio frames presented to an
+     * external observer. This excludes frames which have been written but are
+     * still in the pipeline. The count must not reset to zero when a PCM output
+     * enters standby. For compressed and offload streams it is recommended that
+     * HAL resets the frame count.
+     *
+     * This method also returns the value of CLOCK_MONOTONIC as of this
+     * presentation count. The returned count is expected to be 'recent', but
+     * does not need to be the most recent possible value. However, the
+     * associated time must correspond to whatever count is returned.
+     * Example: assume that N+M frames have been presented, where M is a 'small'
+     * number. Then it is permissible to return N instead of N+M, and the
+     * timestamp must correspond to N rather than N+M. The terms 'recent' and
+     * 'small' are not defined. They reflect the quality of the implementation.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return frames count of presented audio frames.
+     * @return timeStamp associated clock time.
+     */
+    getPresentationPosition()
+            generates (Result retval, uint64_t frames, TimeSpec timeStamp);
+
+    /**
+     * Selects a presentation for decoding from a next generation media stream
+     * (as defined per ETSI TS 103 190-2) and a program within the presentation.
+     *
+     * Optional method
+     *
+     * @param presentationId selected audio presentation.
+     * @param programId refinement for the presentation.
+     * @return retval operation completion status.
+     */
+    selectPresentation(int32_t presentationId, int32_t programId)
+            generates (Result retval);
+
+    /**
+     * Returns the Dual Mono mode presentation setting.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return mode current setting of Dual Mono mode.
+     */
+    getDualMonoMode() generates (Result retval, DualMonoMode mode);
+
+    /**
+     * Sets the Dual Mono mode presentation on the output device.
+     *
+     * The Dual Mono mode is generally applied to stereo audio streams
+     * where the left and right channels come from separate sources.
+     *
+     * Optional method
+     *
+     * @param mode selected Dual Mono mode.
+     * @return retval operation completion status.
+     */
+    setDualMonoMode(DualMonoMode mode) generates (Result retval);
+
+    /**
+     * Returns the Audio Description Mix level in dB.
+     *
+     * The level is applied to streams incorporating a secondary Audio
+     * Description stream. It specifies the relative level of mixing for
+     * the Audio Description with a reference to the Main Audio.
+     *
+     * Optional method
+     *
+     * The value of the relative level is in the range from negative infinity
+     * to +48.
+     *
+     * @return retval operation completion status.
+     * @return leveldB the current Audio Description Mix Level in dB.
+     */
+    getAudioDescriptionMixLevel() generates (Result retval, float leveldB);
+
+    /**
+     * Sets the Audio Description Mix level in dB.
+     *
+     * For streams incorporating a secondary Audio Description stream
+     * the relative level of mixing of the Audio Description to the Main Audio
+     * is controlled by this method.
+     *
+     * Optional method
+     *
+     * The value of the relative level must be in the range from negative
+     * infinity to +48.
+     *
+     * @param leveldB Audio Description Mix Level in dB
+     * @return retval operation completion status.
+     */
+    setAudioDescriptionMixLevel(float leveldB) generates (Result retval);
+
+    /**
+     * Retrieves current playback rate parameters.
+     *
+     * Optional method
+     *
+     * @return retval operation completion status.
+     * @return playbackRate current playback parameters
+     */
+    getPlaybackRateParameters()
+            generates (Result retval, PlaybackRate playbackRate);
+
+    /**
+     * Sets the playback rate parameters that control playback behavior.
+     * This is normally used when playing encoded content and decoding
+     * is performed in hardware. Otherwise, the framework can apply
+     * necessary transformations.
+     *
+     * Optional method
+     *
+     * If the HAL supports setting the playback rate, it is recommended
+     * to support speed and pitch values at least in the range
+     * from 0.5f to 2.0f, inclusive (see the definition of PlaybackRate struct).
+     *
+     * @param playbackRate playback parameters
+     * @return retval operation completion status.
+     */
+    setPlaybackRateParameters(PlaybackRate playbackRate)
+            generates (Result retval);
+};
diff --git a/audio/7.0/IStreamOutCallback.hal b/audio/7.0/IStreamOutCallback.hal
new file mode 100644
index 0000000..7b9d47f
--- /dev/null
+++ b/audio/7.0/IStreamOutCallback.hal
@@ -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.
+ */
+
+package android.hardware.audio@7.0;
+
+/**
+ * Asynchronous write callback interface.
+ */
+interface IStreamOutCallback {
+    /**
+     * Non blocking write completed.
+     */
+    oneway onWriteReady();
+
+    /**
+     * Drain completed.
+     */
+    oneway onDrainReady();
+
+    /**
+     * Stream hit an error.
+     */
+    oneway onError();
+};
diff --git a/audio/7.0/IStreamOutEventCallback.hal b/audio/7.0/IStreamOutEventCallback.hal
new file mode 100644
index 0000000..8ef0060
--- /dev/null
+++ b/audio/7.0/IStreamOutEventCallback.hal
@@ -0,0 +1,159 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+/**
+ * Asynchronous stream out event callback interface. The interface provides
+ * a way for the HAL to notify platform when there are changes, e.g. codec
+ * format change, from the lower layer.
+ */
+interface IStreamOutEventCallback {
+    /**
+     * Codec format changed.
+     *
+     * onCodecFormatChanged returns an AudioMetadata object in read-only ByteString format.
+     * It represents the most recent codec format decoded by a HW audio decoder.
+     *
+     * Codec format is an optional message from HW audio decoders. It serves to
+     * notify the application about the codec format and audio objects contained
+     * within the compressed audio stream for control, informational,
+     * and display purposes.
+     *
+     * audioMetadata ByteString is convertible to an AudioMetadata object through
+     * both a C++ and a C API present in Metadata.h [1], or through a Java API present
+     * in AudioMetadata.java [2].
+     *
+     * The ByteString format is a stable format used for parcelling (marshalling) across
+     * JNI, AIDL, and HIDL interfaces.  The test for R compatibility for native marshalling
+     * is TEST(metadata_tests, compatibility_R) [3].  The test for R compatibility for JNI
+     * marshalling is android.media.cts.AudioMetadataTest#testCompatibilityR [4].
+     *
+     * R (audio HAL 6.0) defined keys are as follows [2]:
+     * "bitrate", int32
+     * "channel-mask", int32
+     * "mime", string
+     * "sample-rate", int32
+     * "bit-width", int32
+     * "has-atmos", int32
+     * "audio-encoding", int32
+     *
+     * S (audio HAL 7.0) in addition adds the following keys:
+     * "presentation-id", int32
+     * "program-id", int32
+     * "presentation-content-classifier", int32
+     *    presentation-content-classifier key values can be referenced from
+     *    frameworks/base/media/java/android/media/AudioPresentation.java
+     *    i.e AudioPresentation.ContentClassifier
+     *    It can contain any of the below values
+     *    CONTENT_UNKNOWN   = -1,
+     *    CONTENT_MAIN      =  0,
+     *    CONTENT_MUSIC_AND_EFFECTS = 1,
+     *    CONTENT_VISUALLY_IMPAIRED = 2,
+     *    CONTENT_HEARING_IMPAIRED  = 3,
+     *    CONTENT_DIALOG = 4,
+     *    CONTENT_COMMENTARY = 5,
+     *    CONTENT_EMERGENCY = 6,
+     *    CONTENT_VOICEOVER = 7
+     * "presentation-language", string  // represents ISO 639-2 (three letter code)
+     *
+     * Parceling Format:
+     * All values are native endian order. [1]
+     *
+     * using type_size_t = uint32_t;
+     * using index_size_t = uint32_t;
+     * using datum_size_t = uint32_t;
+     *
+     * Permitted type indexes are
+     * TYPE_NONE = 0, // Reserved
+     * TYPE_INT32 = 1,
+     * TYPE_INT64 = 2,
+     * TYPE_FLOAT = 3,
+     * TYPE_DOUBLE = 4,
+     * TYPE_STRING = 5,
+     * TYPE_DATA = 6,  // A data table of <String, Datum>
+     *
+     * Datum = {
+     *           (type_size_t)  Type (the type index from type_as_value<T>.)
+     *           (datum_size_t) Size (size of the Payload)
+     *           (byte string)  Payload<Type>
+     *         }
+     *
+     * The data is specified in native endian order.
+     * Since the size of the Payload is always present, unknown types may be skipped.
+     *
+     * Payload<Fixed-size Primitive_Value>
+     * [ sizeof(Primitive_Value) in raw bytes ]
+     *
+     * Example of Payload<Int32> of 123:
+     * Payload<Int32>
+     * [ value of 123                   ] =  0x7b 0x00 0x00 0x00       123
+     *
+     * Payload<String>
+     * [ (index_size_t) length, not including zero terminator.]
+     * [ (length) raw bytes ]
+     *
+     * Example of Payload<String> of std::string("hi"):
+     * [ (index_size_t) length          ] = 0x02 0x00 0x00 0x00        2 strlen("hi")
+     * [ raw bytes "hi"                 ] = 0x68 0x69                  "hi"
+     *
+     * Payload<Data>
+     * [ (index_size_t) entries ]
+     * [ raw bytes   (entry 1) Key   (Payload<String>)
+     *                         Value (Datum)
+     *                ...  (until #entries) ]
+     *
+     * Example of Payload<Data> of {{"hello", "world"},
+     *                              {"value", (int32_t)1000}};
+     * [ (index_size_t) #entries        ] = 0x02 0x00 0x00 0x00        2 entries
+     *    Key (Payload<String>)
+     *    [ index_size_t length         ] = 0x05 0x00 0x00 0x00        5 strlen("hello")
+     *    [ raw bytes "hello"           ] = 0x68 0x65 0x6c 0x6c 0x6f   "hello"
+     *    Value (Datum)
+     *    [ (type_size_t) type          ] = 0x05 0x00 0x00 0x00        5 (TYPE_STRING)
+     *    [ (datum_size_t) size         ] = 0x09 0x00 0x00 0x00        sizeof(index_size_t) +
+     *                                                                 strlen("world")
+     *       Payload<String>
+     *       [ (index_size_t) length    ] = 0x05 0x00 0x00 0x00        5 strlen("world")
+     *       [ raw bytes "world"        ] = 0x77 0x6f 0x72 0x6c 0x64   "world"
+     *    Key (Payload<String>)
+     *    [ index_size_t length         ] = 0x05 0x00 0x00 0x00        5 strlen("value")
+     *    [ raw bytes "value"           ] = 0x76 0x61 0x6c 0x75 0x65   "value"
+     *    Value (Datum)
+     *    [ (type_size_t) type          ] = 0x01 0x00 0x00 0x00        1 (TYPE_INT32)
+     *    [ (datum_size_t) size         ] = 0x04 0x00 0x00 0x00        4 sizeof(int32_t)
+     *        Payload<Int32>
+     *        [ raw bytes 1000          ] = 0xe8 0x03 0x00 0x00        1000
+     *
+     * The contents of audioMetadata is a Payload<Data>.
+     * An implementation dependent detail is that the Keys are always
+     * stored sorted, so the byte string representation generated is unique.
+     *
+     * Vendor keys are allowed for informational and debugging purposes.
+     * Vendor keys should consist of the vendor company name followed
+     * by a dot; for example, "vendorCompany.someVolume" [2].
+     *
+     * [1] system/media/audio_utils/include/audio_utils/Metadata.h
+     * [2] frameworks/base/media/java/android/media/AudioMetadata.java
+     * [3] system/media/audio_utils/tests/metadata_tests.cpp
+     * [4] cts/tests/tests/media/src/android/media/cts/AudioMetadataTest.java
+     *
+     * @param audioMetadata is a buffer containing decoded format changes
+     *     reported by codec. The buffer contains data that can be transformed
+     *     to audio metadata, which is a C++ object based map.
+     */
+    oneway onCodecFormatChanged(vec<uint8_t> audioMetadata);
+};
diff --git a/audio/7.0/config/Android.bp b/audio/7.0/config/Android.bp
new file mode 100644
index 0000000..096ab6a
--- /dev/null
+++ b/audio/7.0/config/Android.bp
@@ -0,0 +1,31 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+xsd_config {
+    name: "audio_policy_configuration_V7_0",
+    srcs: ["audio_policy_configuration.xsd"],
+    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/api/current.txt b/audio/7.0/config/api/current.txt
new file mode 100644
index 0000000..c2585bd
--- /dev/null
+++ b/audio/7.0/config/api/current.txt
@@ -0,0 +1,590 @@
+// Signature format: 2.0
+package android.audio.policy.configuration.V7_0 {
+
+  public class AttachedDevices {
+    ctor public AttachedDevices();
+    method @Nullable public java.util.List<java.lang.String> getItem();
+  }
+
+  public enum AudioChannelMask {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_10;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_11;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_12;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_13;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_14;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_15;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_16;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_17;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_18;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_19;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_20;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_21;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_22;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_23;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_24;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_5;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_6;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_7;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_8;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_INDEX_MASK_9;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_2POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_3POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_5POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_6;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_FRONT_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_STEREO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_CALL_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_13POINT_360RA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_22POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_2POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT0POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_3POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1POINT4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_5POINT1_SIDE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_6POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_7POINT1POINT4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_A;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_PENTA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_BACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_QUAD_SIDE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_SURROUND;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioChannelMask AUDIO_CHANNEL_OUT_TRI_BACK;
+  }
+
+  public enum AudioContentType {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MOVIE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SONIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_SPEECH;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioContentType AUDIO_CONTENT_TYPE_UNKNOWN;
+  }
+
+  public enum AudioDevice {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AMBIENT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_AUX_DIGITAL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BACK_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLE_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_BLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUILTIN_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_BUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_ECHO_REFERENCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_FM_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_ARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_HDMI_EARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_IP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_LOOPBACK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_PROXY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_SPDIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_STUB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TELEPHONY_RX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_TV_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_ACCESSORY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_DEVICE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_USB_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_VOICE_CALL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_IN_WIRED_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_NONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_DIGITAL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_AUX_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLE_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_BUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_EARPIECE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_ECHO_CANCELLER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_FM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_ARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HDMI_EARC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_HEARING_AID;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_IP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_LINE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_PROXY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPDIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_STUB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_TELEPHONY_TX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_ACCESSORY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_DEVICE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_USB_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioDevice AUDIO_DEVICE_OUT_WIRED_HEADSET;
+  }
+
+  public enum AudioEncapsulationType {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_IEC61937;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioEncapsulationType AUDIO_ENCAPSULATION_TYPE_NONE;
+  }
+
+  public enum AudioFormat {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADIF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ELD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_ERLC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_LTP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_MAIN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SCALABLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_SSR;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ADTS_XHE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ELD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_ERLC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_HE_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LATM_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_LTP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_MAIN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SCALABLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_SSR;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AAC_XHE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AC4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_ALAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_NB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_AMR_WB_PLUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_ADAPTIVE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_HD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_APTX_TWSP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_CELT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DOLBY_TRUEHD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DRA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DSD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_HD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_DTS_UHD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCNW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_EVRCWB;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_E_AC3_JOC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_FLAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_HE_AAC_V2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_IEC60958;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_IEC61937;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LC3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LDAC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_LHDC_LL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_1_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MAT_2_1;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP2;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MP3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MPEGH_BL_L3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MPEGH_BL_L4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MPEGH_LC_L3;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_MPEGH_LC_L4;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_OPUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_16_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_24_BIT_PACKED;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_32_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_24_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_8_BIT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_PCM_FLOAT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_QCELP;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_SBC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_VORBIS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioFormat AUDIO_FORMAT_WMA_PRO;
+  }
+
+  public enum AudioGainMode {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_CHANNELS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_JOINT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioGainMode AUDIO_GAIN_MODE_RAMP;
+  }
+
+  public enum AudioInOutFlag {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_DIRECT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_FAST;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_AV_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_HW_HOTWORD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_MMAP_NOIRQ;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_RAW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_INPUT_FLAG_VOIP_TX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DEEP_BUFFER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_DIRECT_PCM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_FAST;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_HW_AV_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_INCALL_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_MMAP_NOIRQ;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_NON_BLOCKING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_PRIMARY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_RAW;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_SYNC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_TTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioInOutFlag AUDIO_OUTPUT_FLAG_VOIP_RX;
+  }
+
+  public class AudioPolicyConfiguration {
+    ctor public AudioPolicyConfiguration();
+    method @Nullable public android.audio.policy.configuration.V7_0.GlobalConfiguration getGlobalConfiguration();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Modules> getModules();
+    method @Nullable public android.audio.policy.configuration.V7_0.SurroundSound getSurroundSound();
+    method @Nullable public android.audio.policy.configuration.V7_0.Version getVersion();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Volumes> getVolumes();
+    method public void setGlobalConfiguration(@Nullable android.audio.policy.configuration.V7_0.GlobalConfiguration);
+    method public void setSurroundSound(@Nullable android.audio.policy.configuration.V7_0.SurroundSound);
+    method public void setVersion(@Nullable android.audio.policy.configuration.V7_0.Version);
+  }
+
+  public enum AudioSource {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_CAMCORDER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_DEFAULT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_ECHO_REFERENCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_FM_TUNER;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_HOTWORD;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_MIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_REMOTE_SUBMIX;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_UNPROCESSED;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_CALL;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_DOWNLINK;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_PERFORMANCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_RECOGNITION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioSource AUDIO_SOURCE_VOICE_UPLINK;
+  }
+
+  public enum AudioStreamType {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ACCESSIBILITY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ALARM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_BLUETOOTH_SCO;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_CALL_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_DTMF;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_ENFORCED_AUDIBLE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_MUSIC;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_NOTIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_PATCH;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_REROUTING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_RING;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_SYSTEM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_TTS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioStreamType AUDIO_STREAM_VOICE_CALL;
+  }
+
+  public enum AudioUsage {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ALARM;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ANNOUNCEMENT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANCE_SONIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_CALL_ASSISTANT;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_EMERGENCY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_GAME;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_MEDIA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_SAFETY;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_UNKNOWN;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VEHICLE_STATUS;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VIRTUAL_SOURCE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION;
+    enum_constant public static final android.audio.policy.configuration.V7_0.AudioUsage AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING;
+  }
+
+  public enum DeviceCategory {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EARPIECE;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_EXT_MEDIA;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEADSET;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_HEARING_AID;
+    enum_constant public static final android.audio.policy.configuration.V7_0.DeviceCategory DEVICE_CATEGORY_SPEAKER;
+  }
+
+  public class DevicePorts {
+    ctor public DevicePorts();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.DevicePorts.DevicePort> getDevicePort();
+  }
+
+  public static class DevicePorts.DevicePort {
+    ctor public DevicePorts.DevicePort();
+    method @Nullable public String getAddress();
+    method @Nullable public java.util.List<java.lang.String> getEncodedFormats();
+    method @Nullable public android.audio.policy.configuration.V7_0.Gains getGains();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Profile> getProfile();
+    method @Nullable public android.audio.policy.configuration.V7_0.Role getRole();
+    method @Nullable public String getTagName();
+    method @Nullable public String getType();
+    method @Nullable public boolean get_default();
+    method public void setAddress(@Nullable String);
+    method public void setEncodedFormats(@Nullable java.util.List<java.lang.String>);
+    method public void setGains(@Nullable android.audio.policy.configuration.V7_0.Gains);
+    method public void setRole(@Nullable android.audio.policy.configuration.V7_0.Role);
+    method public void setTagName(@Nullable String);
+    method public void setType(@Nullable String);
+    method public void set_default(@Nullable boolean);
+  }
+
+  public enum EngineSuffix {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.EngineSuffix _default;
+    enum_constant public static final android.audio.policy.configuration.V7_0.EngineSuffix configurable;
+  }
+
+  public class Gains {
+    ctor public Gains();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Gains.Gain> getGain();
+  }
+
+  public static class Gains.Gain {
+    ctor public Gains.Gain();
+    method @Nullable public android.audio.policy.configuration.V7_0.AudioChannelMask getChannel_mask();
+    method @Nullable public int getDefaultValueMB();
+    method @Nullable public int getMaxRampMs();
+    method @Nullable public int getMaxValueMB();
+    method @Nullable public int getMinRampMs();
+    method @Nullable public int getMinValueMB();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioGainMode> getMode();
+    method @Nullable public String getName();
+    method @Nullable public int getStepValueMB();
+    method @Nullable public boolean getUseForVolume();
+    method public void setChannel_mask(@Nullable android.audio.policy.configuration.V7_0.AudioChannelMask);
+    method public void setDefaultValueMB(@Nullable int);
+    method public void setMaxRampMs(@Nullable int);
+    method public void setMaxValueMB(@Nullable int);
+    method public void setMinRampMs(@Nullable int);
+    method public void setMinValueMB(@Nullable int);
+    method public void setMode(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioGainMode>);
+    method public void setName(@Nullable String);
+    method public void setStepValueMB(@Nullable int);
+    method public void setUseForVolume(@Nullable boolean);
+  }
+
+  public class GlobalConfiguration {
+    ctor public GlobalConfiguration();
+    method @Nullable public boolean getCall_screen_mode_supported();
+    method @Nullable public android.audio.policy.configuration.V7_0.EngineSuffix getEngine_library();
+    method @Nullable public boolean getSpeaker_drc_enabled();
+    method public void setCall_screen_mode_supported(@Nullable boolean);
+    method public void setEngine_library(@Nullable android.audio.policy.configuration.V7_0.EngineSuffix);
+    method public void setSpeaker_drc_enabled(@Nullable boolean);
+  }
+
+  public enum HalVersion {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.HalVersion _2_0;
+    enum_constant public static final android.audio.policy.configuration.V7_0.HalVersion _3_0;
+  }
+
+  public class MixPorts {
+    ctor public MixPorts();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.MixPorts.MixPort> getMixPort();
+  }
+
+  public static class MixPorts.MixPort {
+    ctor public MixPorts.MixPort();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioInOutFlag> getFlags();
+    method @Nullable public android.audio.policy.configuration.V7_0.Gains getGains();
+    method @Nullable public long getMaxActiveCount();
+    method @Nullable public long getMaxOpenCount();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioUsage> getPreferredUsage();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Profile> getProfile();
+    method @Nullable public android.audio.policy.configuration.V7_0.Role getRole();
+    method public void setFlags(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioInOutFlag>);
+    method public void setGains(@Nullable android.audio.policy.configuration.V7_0.Gains);
+    method public void setMaxActiveCount(@Nullable long);
+    method public void setMaxOpenCount(@Nullable long);
+    method public void setName(@Nullable String);
+    method public void setPreferredUsage(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioUsage>);
+    method public void setRole(@Nullable android.audio.policy.configuration.V7_0.Role);
+  }
+
+  public enum MixType {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.MixType mix;
+    enum_constant public static final android.audio.policy.configuration.V7_0.MixType mux;
+  }
+
+  public class Modules {
+    ctor public Modules();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Modules.Module> getModule();
+  }
+
+  public static class Modules.Module {
+    ctor public Modules.Module();
+    method @Nullable public android.audio.policy.configuration.V7_0.AttachedDevices getAttachedDevices();
+    method @Nullable public String getDefaultOutputDevice();
+    method @Nullable public android.audio.policy.configuration.V7_0.DevicePorts getDevicePorts();
+    method @Nullable public android.audio.policy.configuration.V7_0.HalVersion getHalVersion();
+    method @Nullable public android.audio.policy.configuration.V7_0.MixPorts getMixPorts();
+    method @Nullable public String getName();
+    method @Nullable public android.audio.policy.configuration.V7_0.Routes getRoutes();
+    method public void setAttachedDevices(@Nullable android.audio.policy.configuration.V7_0.AttachedDevices);
+    method public void setDefaultOutputDevice(@Nullable String);
+    method public void setDevicePorts(@Nullable android.audio.policy.configuration.V7_0.DevicePorts);
+    method public void setHalVersion(@Nullable android.audio.policy.configuration.V7_0.HalVersion);
+    method public void setMixPorts(@Nullable android.audio.policy.configuration.V7_0.MixPorts);
+    method public void setName(@Nullable String);
+    method public void setRoutes(@Nullable android.audio.policy.configuration.V7_0.Routes);
+  }
+
+  public class Profile {
+    ctor public Profile();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask> getChannelMasks();
+    method @Nullable public android.audio.policy.configuration.V7_0.AudioEncapsulationType getEncapsulationType();
+    method @Nullable public String getFormat();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.math.BigInteger> getSamplingRates();
+    method public void setChannelMasks(@Nullable java.util.List<android.audio.policy.configuration.V7_0.AudioChannelMask>);
+    method public void setEncapsulationType(@Nullable android.audio.policy.configuration.V7_0.AudioEncapsulationType);
+    method public void setFormat(@Nullable String);
+    method public void setName(@Nullable String);
+    method public void setSamplingRates(@Nullable java.util.List<java.math.BigInteger>);
+  }
+
+  public class Reference {
+    ctor public Reference();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.lang.String> getPoint();
+    method public void setName(@Nullable String);
+  }
+
+  public enum Role {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.Role sink;
+    enum_constant public static final android.audio.policy.configuration.V7_0.Role source;
+  }
+
+  public class Routes {
+    ctor public Routes();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Routes.Route> getRoute();
+  }
+
+  public static class Routes.Route {
+    ctor public Routes.Route();
+    method @Nullable public String getSink();
+    method @Nullable public String getSources();
+    method @Nullable public android.audio.policy.configuration.V7_0.MixType getType();
+    method public void setSink(@Nullable String);
+    method public void setSources(@Nullable String);
+    method public void setType(@Nullable android.audio.policy.configuration.V7_0.MixType);
+  }
+
+  public class SurroundFormats {
+    ctor public SurroundFormats();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.SurroundFormats.Format> getFormat();
+  }
+
+  public static class SurroundFormats.Format {
+    ctor public SurroundFormats.Format();
+    method @Nullable public String getName();
+    method @Nullable public java.util.List<java.lang.String> getSubformats();
+    method public void setName(@Nullable String);
+    method public void setSubformats(@Nullable java.util.List<java.lang.String>);
+  }
+
+  public class SurroundSound {
+    ctor public SurroundSound();
+    method @Nullable public android.audio.policy.configuration.V7_0.SurroundFormats getFormats();
+    method public void setFormats(@Nullable android.audio.policy.configuration.V7_0.SurroundFormats);
+  }
+
+  public enum Version {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.audio.policy.configuration.V7_0.Version _7_0;
+  }
+
+  public class Volume {
+    ctor public Volume();
+    method @Nullable public android.audio.policy.configuration.V7_0.DeviceCategory getDeviceCategory();
+    method @Nullable public java.util.List<java.lang.String> getPoint();
+    method @Nullable public String getRef();
+    method @Nullable public android.audio.policy.configuration.V7_0.AudioStreamType getStream();
+    method public void setDeviceCategory(@Nullable android.audio.policy.configuration.V7_0.DeviceCategory);
+    method public void setRef(@Nullable String);
+    method public void setStream(@Nullable android.audio.policy.configuration.V7_0.AudioStreamType);
+  }
+
+  public class Volumes {
+    ctor public Volumes();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Reference> getReference();
+    method @Nullable public java.util.List<android.audio.policy.configuration.V7_0.Volume> getVolume();
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method @Nullable public static android.audio.policy.configuration.V7_0.AudioPolicyConfiguration read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/tests/bar/1.0/.hidl_for_test b/audio/7.0/config/api/last_current.txt
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to audio/7.0/config/api/last_current.txt
diff --git a/tests/bar/1.0/.hidl_for_test b/audio/7.0/config/api/last_removed.txt
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to audio/7.0/config/api/last_removed.txt
diff --git a/audio/7.0/config/api/removed.txt b/audio/7.0/config/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/audio/7.0/config/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/audio/7.0/config/audio_policy_configuration.xsd b/audio/7.0/config/audio_policy_configuration.xsd
new file mode 100644
index 0000000..e0df359
--- /dev/null
+++ b/audio/7.0/config/audio_policy_configuration.xsd
@@ -0,0 +1,813 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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.
+-->
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- List the config versions supported by audio policy. -->
+    <xs:simpleType name="version">
+        <xs:restriction base="xs:decimal">
+            <xs:enumeration value="7.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="halVersion">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Version of the interface the hal implements. Note that this
+                relates to legacy HAL API versions since HIDL APIs are versioned
+                using other mechanisms.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:decimal">
+            <!-- List of HAL versions supported by the framework. -->
+            <xs:enumeration value="2.0"/>
+            <xs:enumeration value="3.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:element name="audioPolicyConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="globalConfiguration" type="globalConfiguration"/>
+                <xs:element name="modules" type="modules" maxOccurs="unbounded"/>
+                <xs:element name="volumes" type="volumes" maxOccurs="unbounded"/>
+                <xs:element name="surroundSound" type="surroundSound" minOccurs="0" />
+            </xs:sequence>
+            <xs:attribute name="version" type="version"/>
+        </xs:complexType>
+        <xs:key name="moduleNameKey">
+            <xs:selector xpath="modules/module"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:unique name="volumeTargetUniqueness">
+            <xs:selector xpath="volumes/volume"/>
+            <xs:field xpath="@stream"/>
+            <xs:field xpath="@deviceCategory"/>
+        </xs:unique>
+        <xs:key name="volumeCurveNameKey">
+            <xs:selector xpath="volumes/reference"/>
+            <xs:field xpath="@name"/>
+        </xs:key>
+        <xs:keyref name="volumeCurveRef" refer="volumeCurveNameKey">
+            <xs:selector xpath="volumes/volume"/>
+            <xs:field xpath="@ref"/>
+        </xs:keyref>
+    </xs:element>
+    <xs:complexType name="globalConfiguration">
+        <xs:attribute name="speaker_drc_enabled" type="xs:boolean" use="required"/>
+        <xs:attribute name="call_screen_mode_supported" type="xs:boolean" use="optional"/>
+        <xs:attribute name="engine_library" type="engineSuffix" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="modules">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                There should be one section per audio HW module present on the platform.
+                Each <module/> contains two mandatory tags: “halVersion” and “name”.
+                The module "name" is the same as in previous .conf file.
+                Each module must contain the following sections:
+                 - <devicePorts/>: a list of device descriptors for all
+                   input and output devices accessible via this module.
+                   This contains both permanently attached devices and removable devices.
+                 - <mixPorts/>: listing all output and input streams exposed by the audio HAL
+                 - <routes/>: list of possible connections between input
+                   and output devices or between stream and devices.
+                   A <route/> is defined by a set of 3 attributes:
+                        -"type": mux|mix means all sources are mutual exclusive (mux) or can be mixed (mix)
+                        -"sink": the sink involved in this route
+                        -"sources": all the sources than can be connected to the sink via this route
+                 - <attachedDevices/>: permanently attached devices.
+                   The attachedDevices section is a list of devices names.
+                   Their names correspond to device names defined in "devicePorts" section.
+                 - <defaultOutputDevice/> is the device to be used when no policy rule applies
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="module" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="attachedDevices" type="attachedDevices" minOccurs="0">
+                            <xs:unique name="attachedDevicesUniqueness">
+                                <xs:selector xpath="item"/>
+                                <xs:field xpath="."/>
+                            </xs:unique>
+                        </xs:element>
+                        <xs:element name="defaultOutputDevice" type="xs:token" minOccurs="0"/>
+                        <xs:element name="mixPorts" type="mixPorts" minOccurs="0"/>
+                        <xs:element name="devicePorts" type="devicePorts" minOccurs="0"/>
+                        <xs:element name="routes" type="routes" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:string" use="required"/>
+                    <xs:attribute name="halVersion" type="halVersion" use="required"/>
+                </xs:complexType>
+                <xs:unique name="mixPortNameUniqueness">
+                    <xs:selector xpath="mixPorts/mixPort"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+                <xs:key name="devicePortNameKey">
+                    <xs:selector xpath="devicePorts/devicePort"/>
+                    <xs:field xpath="@tagName"/>
+                </xs:key>
+                <xs:unique name="devicePortUniqueness">
+                    <xs:selector xpath="devicePorts/devicePort"/>
+                    <xs:field xpath="@type"/>
+                    <xs:field xpath="@address"/>
+                </xs:unique>
+                <xs:keyref name="defaultOutputDeviceRef" refer="devicePortNameKey">
+                    <xs:selector xpath="defaultOutputDevice"/>
+                    <xs:field xpath="."/>
+                </xs:keyref>
+                <xs:keyref name="attachedDeviceRef" refer="devicePortNameKey">
+                    <xs:selector xpath="attachedDevices/item"/>
+                    <xs:field xpath="."/>
+                </xs:keyref>
+                <!-- The following 3 constraints try to make sure each sink port
+                     is reference in one an only one route. -->
+                <xs:key name="routeSinkKey">
+                    <!-- predicate [@type='sink'] does not work in xsd 1.0 -->
+                    <xs:selector xpath="devicePorts/devicePort|mixPorts/mixPort"/>
+                    <xs:field xpath="@tagName|@name"/>
+                </xs:key>
+                <xs:keyref name="routeSinkRef" refer="routeSinkKey">
+                    <xs:selector xpath="routes/route"/>
+                    <xs:field xpath="@sink"/>
+                </xs:keyref>
+                <xs:unique name="routeUniqueness">
+                    <xs:selector xpath="routes/route"/>
+                    <xs:field xpath="@sink"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="attachedDevices">
+        <xs:sequence>
+            <xs:element name="item" type="xs:token" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioInOutFlag">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+              The flags indicate suggested stream attributes supported by the profile.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_PRIMARY" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_FAST" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DEEP_BUFFER" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_NON_BLOCKING" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_HW_AV_SYNC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_TTS" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_RAW" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_SYNC" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_IEC958_NONAUDIO" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_DIRECT_PCM" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_MMAP_NOIRQ" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_VOIP_RX" />
+            <xs:enumeration value="AUDIO_OUTPUT_FLAG_INCALL_MUSIC" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_FAST" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_HW_HOTWORD" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_RAW" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_SYNC" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_MMAP_NOIRQ" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_VOIP_TX" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_HW_AV_SYNC" />
+            <xs:enumeration value="AUDIO_INPUT_FLAG_DIRECT" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioInOutFlags">
+        <xs:list itemType="audioInOutFlag" />
+    </xs:simpleType>
+    <xs:simpleType name="role">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="sink"/>
+            <xs:enumeration value="source"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="mixPorts">
+        <xs:sequence>
+            <xs:element name="mixPort" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+                        <xs:element name="gains" type="gains" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="name" type="xs:token" use="required"/>
+                    <xs:attribute name="role" type="role" use="required"/>
+                    <xs:attribute name="flags" type="audioInOutFlags"/>
+                    <xs:attribute name="maxOpenCount" type="xs:unsignedInt"/>
+                    <xs:attribute name="maxActiveCount" type="xs:unsignedInt"/>
+                    <xs:attribute name="preferredUsage" type="audioUsageList">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">
+                                When choosing the mixPort of an audio track, the audioPolicy
+                                first considers the mixPorts with a preferredUsage including
+                                the track AudioUsage preferred .
+                                If non support the track format, the other mixPorts are considered.
+                                Eg: a <mixPort preferredUsage="AUDIO_USAGE_MEDIA" /> will receive
+                                    the audio of all apps playing with a MEDIA usage.
+                                    It may receive audio from ALARM if there are no audio compatible
+                                    <mixPort preferredUsage="AUDIO_USAGE_ALARM" />.
+                             </xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                </xs:complexType>
+                <xs:unique name="mixPortProfileUniqueness">
+                    <xs:selector xpath="profile"/>
+                    <xs:field xpath="format"/>
+                    <xs:field xpath="samplingRate"/>
+                    <xs:field xpath="channelMasks"/>
+                </xs:unique>
+                <xs:unique name="mixPortGainUniqueness">
+                    <xs:selector xpath="gains/gain"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioDevice">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_DEVICE_NONE"/>
+
+            <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_EARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_BLE_SPEAKER"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_DEFAULT"/>
+            <xs:enumeration value="AUDIO_DEVICE_OUT_STUB"/>
+
+            <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_AMBIENT"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_EARC"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_BLE_HEADSET"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_DEFAULT"/>
+            <xs:enumeration value="AUDIO_DEVICE_IN_STUB"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="vendorExtension">
+        <!-- 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-Z0-9]{3,}_[_A-Z0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="extendableAudioDevice">
+        <xs:union memberTypes="audioDevice vendorExtension"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioFormat">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_FORMAT_DEFAULT" />
+            <xs:enumeration value="AUDIO_FORMAT_PCM_16_BIT" />
+            <xs:enumeration value="AUDIO_FORMAT_PCM_8_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_32_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_8_24_BIT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_FLOAT"/>
+            <xs:enumeration value="AUDIO_FORMAT_PCM_24_BIT_PACKED"/>
+            <xs:enumeration value="AUDIO_FORMAT_MP3"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_NB"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_WB"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_MAIN"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_SSR"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LTP"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_SCALABLE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ERLC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ELD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_XHE"/>
+            <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_HE_AAC_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_VORBIS"/>
+            <xs:enumeration value="AUDIO_FORMAT_OPUS"/>
+            <xs:enumeration value="AUDIO_FORMAT_AC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_E_AC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_E_AC3_JOC"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS_HD"/>
+            <xs:enumeration value="AUDIO_FORMAT_IEC61937"/>
+            <xs:enumeration value="AUDIO_FORMAT_DOLBY_TRUEHD"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRC"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCB"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCWB"/>
+            <xs:enumeration value="AUDIO_FORMAT_EVRCNW"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADIF"/>
+            <xs:enumeration value="AUDIO_FORMAT_WMA"/>
+            <xs:enumeration value="AUDIO_FORMAT_WMA_PRO"/>
+            <xs:enumeration value="AUDIO_FORMAT_AMR_WB_PLUS"/>
+            <xs:enumeration value="AUDIO_FORMAT_MP2"/>
+            <xs:enumeration value="AUDIO_FORMAT_QCELP"/>
+            <xs:enumeration value="AUDIO_FORMAT_DSD"/>
+            <xs:enumeration value="AUDIO_FORMAT_FLAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_ALAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_APE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_MAIN"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SSR"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LTP"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_SCALABLE"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ERLC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_LD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_ELD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_ADTS_XHE"/>
+            <xs:enumeration value="AUDIO_FORMAT_SBC"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_HD"/>
+            <xs:enumeration value="AUDIO_FORMAT_AC4"/>
+            <xs:enumeration value="AUDIO_FORMAT_LDAC"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_1_0"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_2_0"/>
+            <xs:enumeration value="AUDIO_FORMAT_MAT_2_1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_LC"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V1"/>
+            <xs:enumeration value="AUDIO_FORMAT_AAC_LATM_HE_V2"/>
+            <xs:enumeration value="AUDIO_FORMAT_CELT"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_ADAPTIVE"/>
+            <xs:enumeration value="AUDIO_FORMAT_LHDC"/>
+            <xs:enumeration value="AUDIO_FORMAT_LHDC_LL"/>
+            <xs:enumeration value="AUDIO_FORMAT_APTX_TWSP"/>
+            <xs:enumeration value="AUDIO_FORMAT_LC3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_BL_L4"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L3"/>
+            <xs:enumeration value="AUDIO_FORMAT_MPEGH_LC_L4"/>
+            <xs:enumeration value="AUDIO_FORMAT_IEC60958"/>
+            <xs:enumeration value="AUDIO_FORMAT_DTS_UHD"/>
+            <xs:enumeration value="AUDIO_FORMAT_DRA"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="extendableAudioFormat">
+        <xs:union memberTypes="audioFormat vendorExtension"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioUsage">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio usage specifies the intended use case for the sound being played.
+                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_USAGE_UNKNOWN" />
+            <xs:enumeration value="AUDIO_USAGE_MEDIA" />
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION" />
+            <xs:enumeration value="AUDIO_USAGE_VOICE_COMMUNICATION_SIGNALLING" />
+            <xs:enumeration value="AUDIO_USAGE_ALARM" />
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION" />
+            <xs:enumeration value="AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_ACCESSIBILITY" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_NAVIGATION_GUIDANCE" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANCE_SONIFICATION" />
+            <xs:enumeration value="AUDIO_USAGE_GAME" />
+            <xs:enumeration value="AUDIO_USAGE_VIRTUAL_SOURCE" />
+            <xs:enumeration value="AUDIO_USAGE_ASSISTANT" />
+            <xs:enumeration value="AUDIO_USAGE_CALL_ASSISTANT" />
+            <xs:enumeration value="AUDIO_USAGE_EMERGENCY" />
+            <xs:enumeration value="AUDIO_USAGE_SAFETY" />
+            <xs:enumeration value="AUDIO_USAGE_VEHICLE_STATUS" />
+            <xs:enumeration value="AUDIO_USAGE_ANNOUNCEMENT" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioUsageList">
+        <xs:list itemType="audioUsage"/>
+    </xs:simpleType>
+    <xs:simpleType name="audioContentType">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio content type expresses the general category of the content.
+                Please consult frameworks/base/media/java/android/media/AudioAttributes.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_UNKNOWN"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SPEECH"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MUSIC"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_MOVIE"/>
+            <xs:enumeration value="AUDIO_CONTENT_TYPE_SONIFICATION"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="samplingRates">
+        <xs:list itemType="xs:nonNegativeInteger" />
+    </xs:simpleType>
+    <xs:simpleType name="audioChannelMask">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio channel mask specifies presence of particular channels.
+                There are two representations:
+                - representation position (traditional discrete channel specification,
+                  e.g. "left", "right");
+                - indexed (this is similar to "tracks" in audio mixing, channels
+                  are represented using numbers).
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_CHANNEL_NONE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_TRI_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_2POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_3POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_QUAD_SIDE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_SURROUND"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_PENTA"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1_SIDE"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_5POINT1POINT4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_6POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_7POINT1POINT4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_13POINT_360RA"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_22POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_A"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_STEREO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_FRONT_BACK"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_6"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_2POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT0POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_3POINT1POINT2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_5POINT1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_IN_VOICE_CALL_MONO"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_1"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_2"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_3"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_4"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_5"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_6"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_7"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_8"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_9"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_10"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_11"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_12"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_13"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_14"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_15"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_16"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_17"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_18"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_19"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_20"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_21"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_22"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_23"/>
+            <xs:enumeration value="AUDIO_CHANNEL_INDEX_MASK_24"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="channelMasks">
+        <xs:list itemType="audioChannelMask" />
+    </xs:simpleType>
+    <xs:simpleType name="audioEncapsulationType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_NONE"/>
+            <xs:enumeration value="AUDIO_ENCAPSULATION_TYPE_IEC61937"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="profile">
+        <xs:attribute name="name" type="xs:token" use="optional"/>
+        <xs:attribute name="format" type="extendableAudioFormat" use="optional"/>
+        <xs:attribute name="samplingRates" type="samplingRates" use="optional"/>
+        <xs:attribute name="channelMasks" type="channelMasks" use="optional"/>
+        <xs:attribute name="encapsulationType" type="audioEncapsulationType" use="optional"/>
+    </xs:complexType>
+    <xs:simpleType name="audioGainMode">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_GAIN_MODE_JOINT"/>
+            <xs:enumeration value="AUDIO_GAIN_MODE_CHANNELS"/>
+            <xs:enumeration value="AUDIO_GAIN_MODE_RAMP"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioGainModeMaskUnrestricted">
+        <xs:list itemType="audioGainMode" />
+    </xs:simpleType>
+    <xs:simpleType name='audioGainModeMask'>
+        <xs:restriction base='audioGainModeMaskUnrestricted'>
+            <xs:minLength value='1' />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="gains">
+        <xs:sequence>
+            <xs:element name="gain" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:attribute name="name" type="xs:token" use="required"/>
+                    <xs:attribute name="mode" type="audioGainModeMask" use="required"/>
+                    <xs:attribute name="channel_mask" type="audioChannelMask" use="optional"/>
+                    <xs:attribute name="minValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="maxValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="defaultValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="stepValueMB" type="xs:int" use="optional"/>
+                    <xs:attribute name="minRampMs" type="xs:int" use="optional"/>
+                    <xs:attribute name="maxRampMs" type="xs:int" use="optional"/>
+                    <xs:attribute name="useForVolume" type="xs:boolean" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="devicePorts">
+        <xs:sequence>
+            <xs:element name="devicePort" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:sequence>
+                        <xs:element name="profile" type="profile" minOccurs="0" maxOccurs="unbounded"/>
+                        <xs:element name="gains" type="gains" minOccurs="0"/>
+                    </xs:sequence>
+                    <xs:attribute name="tagName" type="xs:token" use="required"/>
+                    <xs:attribute name="type" type="extendableAudioDevice" use="required"/>
+                    <xs:attribute name="role" type="role" use="required"/>
+                    <xs:attribute name="address" type="xs:string" use="optional" default=""/>
+                    <!-- Note that XSD 1.0 can not check that a type only has one default. -->
+                    <xs:attribute name="default" type="xs:boolean" use="optional">
+                        <xs:annotation>
+                            <xs:documentation xml:lang="en">
+                                The default device will be used if multiple have the same type
+                                and no explicit route request exists for a specific device of
+                                that type.
+                            </xs:documentation>
+                        </xs:annotation>
+                    </xs:attribute>
+                    <xs:attribute name="encodedFormats" type="audioFormatsList" use="optional"
+                                  default="" />
+                </xs:complexType>
+                <xs:unique name="devicePortProfileUniqueness">
+                    <xs:selector xpath="profile"/>
+                    <xs:field xpath="format"/>
+                    <xs:field xpath="samplingRate"/>
+                    <xs:field xpath="channelMasks"/>
+                </xs:unique>
+                <xs:unique name="devicePortGainUniqueness">
+                    <xs:selector xpath="gains/gain"/>
+                    <xs:field xpath="@name"/>
+                </xs:unique>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="mixType">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="mix"/>
+            <xs:enumeration value="mux"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="routes">
+        <xs:sequence>
+            <xs:element name="route" minOccurs="0" maxOccurs="unbounded">
+                <xs:annotation>
+                    <xs:documentation xml:lang="en">
+                        List all available sources for a given sink.
+                    </xs:documentation>
+                </xs:annotation>
+                <xs:complexType>
+                    <xs:attribute name="type" type="mixType" use="required"/>
+                    <xs:attribute name="sink" type="xs:string" use="required"/>
+                    <xs:attribute name="sources" type="xs:string" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:complexType name="volumes">
+        <xs:sequence>
+            <xs:element name="volume" type="volume" minOccurs="0" maxOccurs="unbounded"/>
+            <xs:element name="reference" type="reference" minOccurs="0" maxOccurs="unbounded">
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <!-- TODO: Always require a ref for better xsd validations.
+               Currently a volume could have no points nor ref
+               as it can not be forbidden by xsd 1.0.-->
+    <xs:simpleType name="volumePoint">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Comma separated pair of number.
+                The fist one is the framework level (between 0 and 100).
+                The second one is the volume to send to the HAL.
+                The framework will interpolate volumes not specified.
+                Their MUST be at least 2 points specified.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:pattern value="([0-9]{1,2}|100),-?[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioStreamType">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Audio stream type describing the intended use case of a stream.
+                Please consult frameworks/base/media/java/android/media/AudioSystem.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_STREAM_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_STREAM_SYSTEM"/>
+            <xs:enumeration value="AUDIO_STREAM_RING"/>
+            <xs:enumeration value="AUDIO_STREAM_MUSIC"/>
+            <xs:enumeration value="AUDIO_STREAM_ALARM"/>
+            <xs:enumeration value="AUDIO_STREAM_NOTIFICATION"/>
+            <xs:enumeration value="AUDIO_STREAM_BLUETOOTH_SCO"/>
+            <xs:enumeration value="AUDIO_STREAM_ENFORCED_AUDIBLE"/>
+            <xs:enumeration value="AUDIO_STREAM_DTMF"/>
+            <xs:enumeration value="AUDIO_STREAM_TTS"/>
+            <xs:enumeration value="AUDIO_STREAM_ACCESSIBILITY"/>
+            <xs:enumeration value="AUDIO_STREAM_ASSISTANT"/>
+            <xs:enumeration value="AUDIO_STREAM_REROUTING"/>
+            <xs:enumeration value="AUDIO_STREAM_PATCH"/>
+            <xs:enumeration value="AUDIO_STREAM_CALL_ASSISTANT"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="audioSource">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                An audio source defines the intended use case for the sound being recorded.
+                Please consult frameworks/base/media/java/android/media/MediaRecorder.java
+                for the description of each value.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="AUDIO_SOURCE_DEFAULT"/>
+            <xs:enumeration value="AUDIO_SOURCE_MIC"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_UPLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_DOWNLINK"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_CALL"/>
+            <xs:enumeration value="AUDIO_SOURCE_CAMCORDER"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_RECOGNITION"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_COMMUNICATION"/>
+            <xs:enumeration value="AUDIO_SOURCE_REMOTE_SUBMIX"/>
+            <xs:enumeration value="AUDIO_SOURCE_UNPROCESSED"/>
+            <xs:enumeration value="AUDIO_SOURCE_VOICE_PERFORMANCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_ECHO_REFERENCE"/>
+            <xs:enumeration value="AUDIO_SOURCE_FM_TUNER"/>
+            <xs:enumeration value="AUDIO_SOURCE_HOTWORD"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!-- Enum values of device_category from Volume.h. -->
+    <xs:simpleType name="deviceCategory">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DEVICE_CATEGORY_HEADSET"/>
+            <xs:enumeration value="DEVICE_CATEGORY_SPEAKER"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EARPIECE"/>
+            <xs:enumeration value="DEVICE_CATEGORY_EXT_MEDIA"/>
+            <xs:enumeration value="DEVICE_CATEGORY_HEARING_AID"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:complexType name="volume">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Volume section defines a volume curve for a given use case and device category.
+                It contains a list of points of this curve expressing the attenuation in Millibels
+                for a given volume index from 0 to 100.
+                <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </volume>
+
+                It may also reference a reference/@name to avoid duplicating curves.
+                <volume stream="AUDIO_STREAM_MUSIC" deviceCategory="DEVICE_CATEGORY_SPEAKER"
+                        ref="DEFAULT_MEDIA_VOLUME_CURVE"/>
+                <reference name="DEFAULT_MEDIA_VOLUME_CURVE">
+                    <point>0,-9600</point>
+                    <point>100,0</point>
+                </reference>
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="0" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="stream" type="audioStreamType"/>
+        <xs:attribute name="deviceCategory" type="deviceCategory"/>
+        <xs:attribute name="ref" type="xs:token" use="optional"/>
+    </xs:complexType>
+    <xs:complexType name="reference">
+        <xs:sequence>
+            <xs:element name="point" type="volumePoint" minOccurs="2" maxOccurs="unbounded"/>
+        </xs:sequence>
+        <xs:attribute name="name" type="xs:token" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="surroundSound">
+        <xs:annotation>
+            <xs:documentation xml:lang="en">
+                Surround Sound section provides configuration related to handling of
+                multi-channel formats.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:sequence>
+            <xs:element name="formats" type="surroundFormats"/>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="audioFormatsList">
+        <xs:list itemType="extendableAudioFormat" />
+    </xs:simpleType>
+    <xs:complexType name="surroundFormats">
+        <xs:sequence>
+            <xs:element name="format" minOccurs="0" maxOccurs="unbounded">
+                <xs:complexType>
+                    <xs:attribute name="name" type="extendableAudioFormat" use="required"/>
+                    <xs:attribute name="subformats" type="audioFormatsList" />
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+    <xs:simpleType name="engineSuffix">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="default"/>
+            <xs:enumeration value="configurable"/>
+        </xs:restriction>
+    </xs:simpleType>
+</xs:schema>
diff --git a/audio/7.0/config/update_audio_policy_config.sh b/audio/7.0/config/update_audio_policy_config.sh
new file mode 100755
index 0000000..c475dd1
--- /dev/null
+++ b/audio/7.0/config/update_audio_policy_config.sh
@@ -0,0 +1,163 @@
+#!/bin/bash
+
+# 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.
+
+# This script is used to update audio policy configuration files
+# to comply with the updated audio_policy_configuration.xsd from V7.0.
+#
+# The main difference is the separator used in lists for attributes.
+# Since the XML Schema Definition standard only allows space to be
+# used as a separator (see https://www.w3.org/TR/xmlschema11-2/#list-datatypes)
+# the previous versions used a regular expression to validate lists
+# in attribute values. E.g. the channel masks were validated using
+# the following regexp: [_A-Z][_A-Z0-9]*(,[_A-Z][_A-Z0-9]*)*
+# This has an obvious drawback of missing typos in the config file.
+#
+# The V7.0 has shifted to defining most of the frequently changed
+# types in the XSD schema only. This allows for verifying all the values
+# in lists, but in order to comply with XML Schema requirements
+# list elements must be separated by space.
+#
+# Since the APM config files typically use include directives,
+# the script must be pointed to the main APM config file and will
+# take care all the included files automatically.
+# If the included file is a shared version from 'frameworks/av',
+# instead of updating it the script checks if there is a newer
+# version with the corresponding name suffix (e.g.
+# 'a2dp_audio_policy_configuration_7_0.xml') and updates the include
+# path instead.
+
+set -euo pipefail
+
+if (echo "$@" | grep -qe "^-h"); then
+    echo "This script will update Audio Policy Manager config file"
+    echo "to the format required by V7.0 XSD schema from a previous"
+    echo "version."
+    echo
+    echo "USAGE: $0 [APM_XML_FILE] [OLD_VERSION]"
+    echo "       APM_XML_FILE specifies the path to audio_policy_configuration.xml"
+    echo "                    relative to Android repository root"
+    echo "       OLD_VERSION specifies the version of schema currently used"
+    echo
+    echo "Example: $0 device/generic/goldfish/audio/policy/audio_policy_configuration.xml 6.0"
+    exit
+fi
+readonly HAL_DIRECTORY=hardware/interfaces/audio
+readonly SHARED_CONFIGS_DIRECTORY=frameworks/av/services/audiopolicy/config
+readonly OLD_VERSION=${2:-$(ls ${ANDROID_BUILD_TOP}/${HAL_DIRECTORY} | grep -E '[0-9]+\.[0-9]+' |
+                                sort -n | tail -n1)}
+readonly NEW_VERSION=7.0
+readonly NEW_VERSION_UNDERSCORE=7_0
+
+readonly SOURCE_CONFIG=${ANDROID_BUILD_TOP}/$1
+
+# First, validate the input using the schema of the current version
+
+echo Validating the source against the $OLD_VERSION schema
+xmllint --noout --xinclude \
+        --nofixup-base-uris --path "$ANDROID_BUILD_TOP/$SHARED_CONFIGS_DIRECTORY" \
+        --schema ${ANDROID_BUILD_TOP}/${HAL_DIRECTORY}/${OLD_VERSION}/config/audio_policy_configuration.xsd \
+        ${SOURCE_CONFIG}
+if [ $? -ne 0 ]; then
+    echo
+    echo "Config file fails validation for the specified version $OLD_VERSION--unsafe to update"
+    exit 1
+fi
+
+# Find all the source files recursively
+
+SOURCE_FILES=${SOURCE_CONFIG}
+SHARED_FILES=
+findIncludes() {
+    local FILES_TO_CHECK=
+    for F in $1; do
+        local FOUND_INCLUDES=$(grep -Po '<xi:include href="\K[^"]+(?="\/>)' ${F})
+        for I in ${FOUND_INCLUDES}; do
+            SOURCE_FULL_PATH=$(dirname ${F})/${I}
+            SHARED_FULL_PATH=${ANDROID_BUILD_TOP}/${SHARED_CONFIGS_DIRECTORY}/${I}
+            if [ -f "$SOURCE_FULL_PATH" ]; then
+                # Device-specific file.
+                SOURCE_FILES+=$'\n'${SOURCE_FULL_PATH}
+                FILES_TO_CHECK+=$'\n'${SOURCE_FULL_PATH}
+            elif [ -f "$SHARED_FULL_PATH" ]; then
+                # Shared file from the frameworks repo.
+                SHARED_FILES+=$'\n'${I}
+                FILES_TO_CHECK+=$'\n'${SHARED_FULL_PATH}
+            else
+                echo
+                echo "Include file not found: $I"
+                exit 1
+            fi
+        done
+    done
+    if [ "$FILES_TO_CHECK" ]; then
+        findIncludes "$FILES_TO_CHECK"
+    fi
+}
+findIncludes ${SOURCE_FILES}
+
+echo "Will update $1 and included device-specific files in place."
+echo "Will update paths to shared included files."
+echo "Press Ctrl-C to cancel, Enter to continue"
+read
+
+# Update 'audioPolicyConfiguration version="1.0"' -> 7.0 in the main file
+sed -i -r -e 's/(audioPolicyConfiguration version=")1.0/\17.0/' ${SOURCE_CONFIG}
+
+updateFile() {
+    FILE=$1
+    ATTR=$2
+    SEPARATOR=$3
+    SRC_LINES=$(grep -nPo "$ATTR=\"[^\"]+\"" ${FILE} || true)
+    for S in $SRC_LINES; do
+        # Prepare instruction for 'sed' for in-place editing of specified line
+        R=$(echo ${S} | sed -e 's/^[0-9]\+:/\//' | sed -e "s/$SEPARATOR/ /g")
+        S=$(echo ${S} | sed -e 's/:/s\//')${R}/
+        echo ${S} | sed -i -f - ${FILE}
+    done
+}
+for F in $SOURCE_FILES; do
+    updateFile ${F} "channelMasks" ","
+    updateFile ${F} "samplingRates" ","
+    updateFile ${F} "flags" "|"
+done;
+
+updateIncludes() {
+    FILE=$1
+    for I in $SHARED_FILES; do
+        NEW_VERSION_I=${I%.*}_${NEW_VERSION_UNDERSCORE}.${I##*.}
+        if [ -e "$ANDROID_BUILD_TOP/$SHARED_CONFIGS_DIRECTORY/$NEW_VERSION_I" ]; then
+            echo "s/$I/$NEW_VERSION_I/g" | sed -i -f - ${FILE}
+        fi
+    done
+}
+for F in $SOURCE_FILES; do
+    updateIncludes ${F}
+done
+
+# Validate the results against the new schema
+
+echo Validating the result against the $NEW_VERSION schema
+xmllint --noout --xinclude \
+        --nofixup-base-uris --path "$ANDROID_BUILD_TOP/$SHARED_CONFIGS_DIRECTORY" \
+        --schema ${ANDROID_BUILD_TOP}/${HAL_DIRECTORY}/${NEW_VERSION}/config/audio_policy_configuration.xsd \
+        ${SOURCE_CONFIG}
+if [ $? -ne 0 ]; then
+    echo
+    echo "Config file fails validation for the specified version $NEW_VERSION--please check the changes"
+    exit 1
+fi
+echo
+echo "Please check the diff and update path to APM shared files in the device makefile!"
diff --git a/audio/7.0/types.hal b/audio/7.0/types.hal
new file mode 100644
index 0000000..2d42129
--- /dev/null
+++ b/audio/7.0/types.hal
@@ -0,0 +1,386 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio@7.0;
+
+import android.hardware.audio.common@7.0;
+
+enum Result : int32_t {
+    OK,
+    NOT_INITIALIZED,
+    INVALID_ARGUMENTS,
+    INVALID_STATE,
+    /**
+     * Methods marked as "Optional method" must return this result value
+     * if the operation is not supported by HAL.
+     */
+    NOT_SUPPORTED
+};
+
+enum AudioDrain : int32_t {
+    /** drain() returns when all data has been played. */
+    ALL,
+    /**
+     * drain() returns a short time before all data from the current track has
+     * been played to give time for gapless track switch.
+     */
+    EARLY_NOTIFY
+};
+
+/**
+ * A substitute for POSIX timespec.
+ */
+struct TimeSpec {
+    /** Seconds. */
+    uint64_t tvSec;
+    /** Nanoseconds. */
+    uint64_t tvNSec;
+};
+
+struct ParameterValue {
+    string key;
+    string value;
+};
+
+enum MmapBufferFlag : uint32_t {
+    NONE    = 0x0,
+    /**
+     * If the buffer can be securely shared to untrusted applications
+     * through the AAudio exclusive mode.
+     * Only set this flag if applications are restricted from accessing the
+     * memory surrounding the audio data buffer by a kernel mechanism.
+     * See Linux kernel's dma_buf.
+     */
+    APPLICATION_SHAREABLE    = 0x1,
+};
+
+/**
+ * Mmap buffer descriptor returned by IStream.createMmapBuffer().
+ * Used by streams opened in mmap mode.
+ */
+struct MmapBufferInfo {
+    /** Mmap memory buffer */
+    memory  sharedMemory;
+    /** Total buffer size in frames */
+    uint32_t bufferSizeFrames;
+    /** Transfer size granularity in frames */
+    uint32_t burstSizeFrames;
+    /** Attributes describing the buffer. */
+    bitfield<MmapBufferFlag> flags;
+};
+
+/**
+ * Mmap buffer read/write position returned by IStream.getMmapPosition().
+ * Used by streams opened in mmap mode.
+ */
+struct MmapPosition {
+    /** Timestamp in ns, CLOCK_MONOTONIC. */
+    int64_t  timeNanoseconds;
+    /** Increasing 32 bit frame count reset when IStream.stop() is called. */
+    int32_t  positionFrames;
+};
+
+/**
+ * The message queue flags used to synchronize reads and writes from
+ * message queues used by StreamIn and StreamOut.
+ */
+enum MessageQueueFlagBits : uint32_t {
+    NOT_EMPTY = 1 << 0,
+    NOT_FULL = 1 << 1
+};
+
+/*
+ * Microphone information
+ *
+ */
+
+/**
+ * A 3D point used to represent position or orientation of a microphone.
+ *
+ * Position: Coordinates of the microphone's capsule, in meters, from the
+ * bottom-left-back corner of the bounding box of android device in natural
+ * orientation (PORTRAIT for phones, LANDSCAPE for tablets, tvs, etc).
+ * The orientation musth match the reported by the api Display.getRotation().
+ *
+ * Orientation: Normalized vector to signal the main orientation of the
+ * microphone's capsule. Magnitude = sqrt(x^2 + y^2 + z^2) = 1
+ */
+struct AudioMicrophoneCoordinate {
+    float x;
+    float y;
+    float z;
+};
+
+/**
+ * Enum to identify the type of channel mapping for active microphones.
+ * Used channels further identify if the microphone has any significative
+ * process (e.g. High Pass Filtering, dynamic compression)
+ * Simple processing as constant gain adjustment must be DIRECT.
+ */
+@export(name="audio_microphone_channel_mapping_t", value_prefix="AUDIO_MICROPHONE_CHANNEL_MAPPING_")
+enum AudioMicrophoneChannelMapping : uint32_t {
+    /** Channel not used. */
+    UNUSED      = 0,
+    /** Channel used and signal not processed. */
+    DIRECT      = 1,
+    /** Channel used and signal has some processing. */
+    PROCESSED   = 2,
+};
+
+/**
+ * Enum to identify locations of microphones in regards to the body of the
+ * android device.
+ */
+@export(name="audio_microphone_location_t", value_prefix="AUDIO_MICROPHONE_LOCATION_")
+enum AudioMicrophoneLocation : uint32_t {
+    UNKNOWN             = 0,
+    MAINBODY            = 1,
+    MAINBODY_MOVABLE    = 2,
+    PERIPHERAL          = 3,
+};
+
+/**
+ * Identifier to help group related microphones together
+ * e.g. microphone arrays should belong to the same group
+ */
+typedef int32_t AudioMicrophoneGroup;
+
+/**
+ * Enum with standard polar patterns of microphones
+ */
+@export(name="audio_microphone_directionality_t", value_prefix="AUDIO_MICROPHONE_DIRECTIONALITY_")
+enum AudioMicrophoneDirectionality : uint32_t {
+    UNKNOWN         = 0,
+    OMNI            = 1,
+    BI_DIRECTIONAL  = 2,
+    CARDIOID        = 3,
+    HYPER_CARDIOID  = 4,
+    SUPER_CARDIOID  = 5,
+};
+
+/**
+ * A (frequency, level) pair. Used to represent frequency response.
+ */
+struct AudioFrequencyResponsePoint {
+    /** In Hz */
+    float frequency;
+    /** In dB */
+    float level;
+};
+
+/**
+ * Structure used by the HAL to describe microphone's characteristics
+ * Used by StreamIn and Device
+ */
+struct MicrophoneInfo {
+    /**
+     * Unique alphanumeric id for microphone. Guaranteed to be the same
+     * even after rebooting.
+     */
+    string                                  deviceId;
+    /**
+     * Device specific information
+     */
+    DeviceAddress                           deviceAddress;
+    /**
+     * Each element of the vector must describe the channel with the same
+     * index.
+     */
+    vec<AudioMicrophoneChannelMapping>      channelMapping;
+    /** Location of the microphone in regard to the body of the device */
+    AudioMicrophoneLocation                 location;
+    /**
+     * Identifier to help group related microphones together
+     * e.g. microphone arrays should belong to the same group
+     */
+    AudioMicrophoneGroup                    group;
+    /**
+     * Index of this microphone within the group.
+     * (group, index) must be unique within the same device.
+     */
+    uint32_t                                indexInTheGroup;
+    /** Level in dBFS produced by a 1000 Hz tone at 94 dB SPL */
+    float                                   sensitivity;
+    /** Level in dB of the max SPL supported at 1000 Hz */
+    float                                   maxSpl;
+    /** Level in dB of the min SPL supported at 1000 Hz */
+    float                                   minSpl;
+    /** Standard polar pattern of the microphone */
+    AudioMicrophoneDirectionality           directionality;
+    /**
+     * Vector with ordered frequency responses (from low to high frequencies)
+     * with the frequency response of the microphone.
+     * Levels are in dB, relative to level at 1000 Hz
+     */
+    vec<AudioFrequencyResponsePoint>        frequencyResponse;
+    /**
+     * Position of the microphone's capsule in meters, from the
+     * bottom-left-back corner of the bounding box of device.
+     */
+    AudioMicrophoneCoordinate               position;
+    /**
+     * Normalized point to signal the main orientation of the microphone's
+     * capsule. sqrt(x^2 + y^2 + z^2) = 1
+     */
+    AudioMicrophoneCoordinate               orientation;
+};
+
+/**
+ * Constants used by the HAL to determine how to select microphones and process those inputs in
+ * order to optimize for capture in the specified direction.
+ *
+ * MicrophoneDirection Constants are defined in MicrophoneDirection.java.
+ */
+@export(name="audio_microphone_direction_t", value_prefix="MIC_DIRECTION_")
+enum MicrophoneDirection : int32_t {
+    /**
+     * Don't do any directionality processing of the activated microphone(s).
+     */
+    UNSPECIFIED = 0,
+    /**
+     * Optimize capture for audio coming from the screen-side of the device.
+     */
+    FRONT = 1,
+    /**
+     * Optimize capture for audio coming from the side of the device opposite the screen.
+     */
+    BACK = 2,
+    /**
+     * Optimize capture for audio coming from an off-device microphone.
+     */
+    EXTERNAL = 3,
+};
+
+
+/* Dual Mono handling is used when a stereo audio stream
+ * contains separate audio content on the left and right channels.
+ * Such information about the content of the stream may be found, for example,
+ * in ITU T-REC-J.94-201610 A.6.2.3 Component descriptor.
+ */
+@export(name="audio_dual_mono_mode_t", value_prefix="AUDIO_DUAL_MONO_MODE_")
+enum DualMonoMode : int32_t {
+    // Need to be in sync with DUAL_MONO_MODE* constants in
+    // frameworks/base/media/java/android/media/AudioTrack.java
+    /**
+     * Disable any Dual Mono presentation effect.
+     */
+    OFF = 0,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left and right audio channels blended together
+     * and delivered to both channels.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that the left-right stereo symmetric
+     * channels are pairwise blended, the other channels such as center
+     * are left alone.
+     */
+    LR = 1,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the left audio channel replicated into the right audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the left channel position replicated
+     * into the right channel position. The center channels (with no
+     * left/right symmetry) or unbalanced channels are left alone.
+     */
+    LL = 2,
+    /**
+     * This mode indicates that a stereo stream should be presented
+     * with the right audio channel replicated into the left audio channel.
+     *
+     * Behavior for non-stereo streams is implementation defined.
+     * A suggested guideline is that all channels with left-right
+     * stereo symmetry will have the right channel position replicated
+     * into the left channel position. The center channels (with no
+     * left/right symmetry) or unbalanced channels are left alone.
+     */
+    RR = 3,
+};
+
+/**
+ * Algorithms used for timestretching (preserving pitch while playing audio
+ * content at different speed).
+ */
+@export(name="audio_timestretch_stretch_mode_t", value_prefix="AUDIO_TIMESTRETCH_STRETCH_")
+enum TimestretchMode : int32_t {
+    // Need to be in sync with AUDIO_STRETCH_MODE_* constants in
+    // frameworks/base/media/java/android/media/PlaybackParams.java
+    DEFAULT = 0,
+    /** Selects timestretch algorithm best suitable for voice (speech) content. */
+    VOICE = 1,
+};
+
+/**
+ * Behavior when the values for speed and / or pitch are out
+ * of applicable range.
+ */
+@export(name="", value_prefix="HAL_AUDIO_TIMESTRETCH_FALLBACK_")
+enum TimestretchFallbackMode : int32_t {
+    // Need to be in sync with AUDIO_FALLBACK_MODE_* constants in
+    // frameworks/base/media/java/android/media/PlaybackParams.java
+    /** Play silence for parameter values that are out of range. */
+    MUTE = 1,
+    /** Return an error while trying to set the parameters. */
+    FAIL = 2,
+};
+
+/**
+ * Parameters determining playback behavior. They are used to speed up or
+ * slow down playback and / or change the tonal frequency of the audio content
+ * (pitch).
+ */
+struct PlaybackRate {
+    /**
+     * Speed factor (multiplier). Normal speed has the value of 1.0f.
+     * Values less than 1.0f slow down playback, value greater than 1.0f
+     * speed it up.
+     */
+    float speed;
+    /**
+     * Pitch factor (multiplier). Setting pitch value to 1.0f together
+     * with changing playback speed preserves the pitch, this is often
+     * called "timestretching." Setting the pitch value equal to speed produces
+     * the same effect as playing audio content at different sampling rate.
+     */
+    float pitch;
+    /**
+     * Selects the algorithm used for timestretching (preserving pitch while
+     * playing audio at different speed).
+     */
+    TimestretchMode timestretchMode;
+    /**
+     * Selects the behavior when the specified values for speed and / or pitch
+     * are out of applicable range.
+     */
+    TimestretchFallbackMode fallbackMode;
+};
+
+/**
+ * The audio flags serve two purposes:
+ *
+ *  - when a stream is created they indicate its attributes;
+ *
+ *  - when present in a profile descriptor listed for a particular audio
+ *    hardware module, they indicate that a stream can be opened that
+ *    supports the attributes indicated by the flags.
+ *
+ * See 'audioIoFlag' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioInOutFlag;
diff --git a/audio/README b/audio/README
deleted file mode 100644
index afafbe3..0000000
--- a/audio/README
+++ /dev/null
@@ -1,36 +0,0 @@
-Directory structure of the audio HIDL related code.
-
-Run `common/all-versions/copyHAL.sh` to create a new version of the audio HAL
-based on an existing one.
-
-audio
-|-- 2.0              <== core 2.0 HIDL API. .hal can not be moved into the core directory
-|                        because that would change its namespace and include path
-|-- 4.0              <== Version 4.0 of the core API
-|
-|-- ...
-|
-|-- common           <== code common to audio core and effect API
-|   |-- 2.0          <== HIDL API of V2
-|   |-- 4.0
-|   |-- ...
-|   `-- all-versions <== code common to all version of both core and effect API
-|       |-- default  <== implementation shared code between core and effect impl
-|       |-- test     <== utilities used by tests
-|       `-- util     <== utilities used by both implementation and tests
-|
-|-- core             <== VTS and default implementation of the core API (not HIDL, see /audio/2.0))
-|   `-- all-versions <== Code is version independent through #if and separate files
-|       |-- default  <== code that wraps the legacy API
-|       `-- vts      <== vts of core API
-|           |-- 2.0  <== 2.0 specific tests and helpers
-|           |-- 4.0
-|           |-- ...
-|
-`-- effect           <== idem for the effect API
-    |-- 2.0
-    |-- 4.0
-    |-- ...
-    `-- all-versions
-        |-- default
-        `-- vts
diff --git a/audio/README.md b/audio/README.md
new file mode 100644
index 0000000..1938ad4
--- /dev/null
+++ b/audio/README.md
@@ -0,0 +1,55 @@
+# Audio HAL
+
+Directory structure of the audio HAL related code.
+
+Run `common/all-versions/copyHAL.sh` to create a new version of the audio HAL
+based on an existing one.
+
+## Directory Structure
+
+* `2.0` — version 2.0 of the core HIDL API. Note that `.hal` files
+  can not be moved into the `core` directory because that would change
+  its namespace and include path.
+   - `config` — the XSD schema for the Audio Policy Manager
+     configuration file.
+* `4.0` — version 4.0 of the core HIDL API.
+* ...
+* `common` — common types for audio core and effect HIDL API.
+   - `2.0` — version 2.0 of the common types HIDL API.
+   - `4.0` — version 4.0.
+   - ...
+   - `7.0` — version 7.0.
+      - `example` — example implementation of the core and effect
+        V7.0 API. It represents a "fake" audio HAL that doesn't
+        actually communicate with hardware.
+   - `all-versions` — code common to all version of both core and effect API.
+      - `default` — shared code of the default implementation.
+         - `service` — vendor HAL service for hosting the default
+           implementation.
+      - `test` — utilities used by tests.
+      - `util` — utilities used by both implementation and tests.
+* `core` — VTS tests and the default implementation of the core API
+  (not HIDL API, it's in `audio/N.M`).
+   - `7.0` — code specific to version V7.0 of the core HIDL API
+   - `all-versions` — the code is common between all versions,
+     version-specific parts are enclosed into conditional directives
+     of preprocessor or reside in dedicated files.
+       - `default` — code that wraps the legacy API (from
+         `hardware/libhardware`).
+         - `util` — utilities for the default implementation.
+       - `vts` VTS tests for the core HIDL API.
+* `effect` — same for the effect HIDL API.
+   - `2.0`
+      - `config` — the XSD schema for the Audio Effects configuration file.
+   - `4.0`
+   - ...
+   - `all-versions`
+       - `default` — code that wraps the legacy API (from
+         `hardware/libhardware`).
+         - `util` — utilities for the default implementation.
+       - `vts` VTS tests for the effect HIDL API.
+* `policy` — Configurable Audio Policy schemes.
+   - `1.0` — note that versions of CAP are not linked to the versions
+     of audio HAL.
+      - `vts` — VTS tests for validating actual configuration files.
+      - `xml` — XSD schemas for CAP configuration files.
diff --git a/audio/common/2.0/Android.bp b/audio/common/2.0/Android.bp
index 56b43ff..f27eb93 100644
--- a/audio/common/2.0/Android.bp
+++ b/audio/common/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.common@2.0",
     root: "android.hardware",
diff --git a/audio/common/4.0/Android.bp b/audio/common/4.0/Android.bp
index e4676ec..ea88b06 100644
--- a/audio/common/4.0/Android.bp
+++ b/audio/common/4.0/Android.bp
@@ -1,13 +1,19 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.common@4.0",
     root: "android.hardware",
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/audio/common/5.0/Android.bp b/audio/common/5.0/Android.bp
index 761c171..c4f3d4c 100644
--- a/audio/common/5.0/Android.bp
+++ b/audio/common/5.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.common@5.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/audio/common/6.0/Android.bp b/audio/common/6.0/Android.bp
index 94f1cf8..fc54caf 100644
--- a/audio/common/6.0/Android.bp
+++ b/audio/common/6.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.common@6.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/audio/common/7.0/Android.bp b/audio/common/7.0/Android.bp
new file mode 100644
index 0000000..2f7665e
--- /dev/null
+++ b/audio/common/7.0/Android.bp
@@ -0,0 +1,38 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.audio.common@7.0",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+    ],
+    interfaces: [
+        "android.hidl.safe_union@1.0",
+    ],
+    gen_java: true,
+    gen_java_constants: true,
+}
+
+cc_library {
+    name: "android.hardware.audio.common@7.0-enums",
+    vendor_available: true,
+    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_enums"],
+    export_header_lib_headers: ["libxsdc-utils"],
+    export_include_dirs: ["enums/include"],
+    shared_libs: [
+        "libbase",
+        "liblog",
+    ],
+}
diff --git a/audio/common/7.0/enums/OWNERS b/audio/common/7.0/enums/OWNERS
new file mode 100644
index 0000000..24071af
--- /dev/null
+++ b/audio/common/7.0/enums/OWNERS
@@ -0,0 +1,2 @@
+elaurent@google.com
+mnaganov@google.com
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
new file mode 100644
index 0000000..a92a277
--- /dev/null
+++ b/audio/common/7.0/enums/include/android_audio_policy_configuration_V7_0-enums.h
@@ -0,0 +1,300 @@
+/*
+ * 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_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
+#define ANDROID_AUDIO_POLICY_CONFIGURATION_V7_0__ENUMS_H
+
+#include <sys/types.h>
+#include <regex>
+#include <string>
+
+#include <android_audio_policy_configuration_V7_0_enums.h>
+
+namespace android::audio::policy::configuration::V7_0 {
+
+static inline size_t getChannelCount(AudioChannelMask mask) {
+    switch (mask) {
+        case AudioChannelMask::AUDIO_CHANNEL_NONE:
+            return 0;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_MONO:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_MONO:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_1:
+            return 1;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_MONO_HAPTIC_A:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_HAPTIC_AB:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_STEREO:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_FRONT_BACK:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_VOICE_UPLINK_MONO:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_VOICE_DNLINK_MONO:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_VOICE_CALL_MONO:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_2:
+            return 2;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_2POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO_HAPTIC_A:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_MONO_HAPTIC_AB:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_TRI:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_TRI_BACK:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_3:
+            return 3;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_2POINT0POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_3POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_QUAD:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_QUAD_BACK:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_QUAD_SIDE:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_SURROUND:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO_HAPTIC_AB:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_2POINT0POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_4:
+            return 4;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_2POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_3POINT0POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_PENTA:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_2POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_3POINT0POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_5:
+            return 5;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_3POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_5POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_5POINT1_BACK:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_5POINT1_SIDE:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_6:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_3POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_IN_5POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_6:
+            return 6;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_6POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_7:
+            return 7;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_5POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_7POINT1:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_8:
+            return 8;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_9:
+            return 9;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_5POINT1POINT4:
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_7POINT1POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_10:
+            return 10;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_11:
+            return 11;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_7POINT1POINT4:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_12:
+            return 12;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_13POINT_360RA:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_13:
+            return 13;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_14:
+            return 14;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_15:
+            return 15;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_16:
+            return 16;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_17:
+            return 17;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_18:
+            return 18;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_19:
+            return 19;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_20:
+            return 20;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_21:
+            return 21;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_22:
+            return 22;
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_23:
+            return 23;
+        case AudioChannelMask::AUDIO_CHANNEL_OUT_22POINT2:
+        case AudioChannelMask::AUDIO_CHANNEL_INDEX_MASK_24:
+            return 24;
+        case AudioChannelMask::UNKNOWN:
+            return 0;
+            // No default to make sure all cases are covered.
+    }
+    // This is to avoid undefined behavior if 'mask' isn't a valid enum value.
+    return 0;
+}
+
+static inline ssize_t getChannelCount(const std::string& mask) {
+    return getChannelCount(stringToAudioChannelMask(mask));
+}
+
+static inline bool isOutputDevice(AudioDevice device) {
+    switch (device) {
+        case AudioDevice::UNKNOWN:
+        case AudioDevice::AUDIO_DEVICE_NONE:
+            return false;
+        case AudioDevice::AUDIO_DEVICE_OUT_EARPIECE:
+        case AudioDevice::AUDIO_DEVICE_OUT_SPEAKER:
+        case AudioDevice::AUDIO_DEVICE_OUT_WIRED_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_WIRED_HEADPHONE:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_SCO:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER:
+        case AudioDevice::AUDIO_DEVICE_OUT_AUX_DIGITAL:
+        case AudioDevice::AUDIO_DEVICE_OUT_HDMI:
+        case AudioDevice::AUDIO_DEVICE_OUT_ANLG_DOCK_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_DGTL_DOCK_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_USB_ACCESSORY:
+        case AudioDevice::AUDIO_DEVICE_OUT_USB_DEVICE:
+        case AudioDevice::AUDIO_DEVICE_OUT_REMOTE_SUBMIX:
+        case AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX:
+        case AudioDevice::AUDIO_DEVICE_OUT_LINE:
+        case AudioDevice::AUDIO_DEVICE_OUT_HDMI_ARC:
+        case AudioDevice::AUDIO_DEVICE_OUT_HDMI_EARC:
+        case AudioDevice::AUDIO_DEVICE_OUT_SPDIF:
+        case AudioDevice::AUDIO_DEVICE_OUT_FM:
+        case AudioDevice::AUDIO_DEVICE_OUT_AUX_LINE:
+        case AudioDevice::AUDIO_DEVICE_OUT_SPEAKER_SAFE:
+        case AudioDevice::AUDIO_DEVICE_OUT_IP:
+        case AudioDevice::AUDIO_DEVICE_OUT_BUS:
+        case AudioDevice::AUDIO_DEVICE_OUT_PROXY:
+        case AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_HEARING_AID:
+        case AudioDevice::AUDIO_DEVICE_OUT_ECHO_CANCELLER:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLE_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_OUT_BLE_SPEAKER:
+        case AudioDevice::AUDIO_DEVICE_OUT_DEFAULT:
+        case AudioDevice::AUDIO_DEVICE_OUT_STUB:
+            return true;
+        case AudioDevice::AUDIO_DEVICE_IN_COMMUNICATION:
+        case AudioDevice::AUDIO_DEVICE_IN_AMBIENT:
+        case AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC:
+        case AudioDevice::AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_WIRED_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_AUX_DIGITAL:
+        case AudioDevice::AUDIO_DEVICE_IN_HDMI:
+        case AudioDevice::AUDIO_DEVICE_IN_VOICE_CALL:
+        case AudioDevice::AUDIO_DEVICE_IN_TELEPHONY_RX:
+        case AudioDevice::AUDIO_DEVICE_IN_BACK_MIC:
+        case AudioDevice::AUDIO_DEVICE_IN_REMOTE_SUBMIX:
+        case AudioDevice::AUDIO_DEVICE_IN_ANLG_DOCK_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_USB_ACCESSORY:
+        case AudioDevice::AUDIO_DEVICE_IN_USB_DEVICE:
+        case AudioDevice::AUDIO_DEVICE_IN_FM_TUNER:
+        case AudioDevice::AUDIO_DEVICE_IN_TV_TUNER:
+        case AudioDevice::AUDIO_DEVICE_IN_LINE:
+        case AudioDevice::AUDIO_DEVICE_IN_SPDIF:
+        case AudioDevice::AUDIO_DEVICE_IN_BLUETOOTH_A2DP:
+        case AudioDevice::AUDIO_DEVICE_IN_LOOPBACK:
+        case AudioDevice::AUDIO_DEVICE_IN_IP:
+        case AudioDevice::AUDIO_DEVICE_IN_BUS:
+        case AudioDevice::AUDIO_DEVICE_IN_PROXY:
+        case AudioDevice::AUDIO_DEVICE_IN_USB_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_BLUETOOTH_BLE:
+        case AudioDevice::AUDIO_DEVICE_IN_HDMI_ARC:
+        case AudioDevice::AUDIO_DEVICE_IN_HDMI_EARC:
+        case AudioDevice::AUDIO_DEVICE_IN_ECHO_REFERENCE:
+        case AudioDevice::AUDIO_DEVICE_IN_BLE_HEADSET:
+        case AudioDevice::AUDIO_DEVICE_IN_DEFAULT:
+        case AudioDevice::AUDIO_DEVICE_IN_STUB:
+            return false;
+            // No default to make sure all cases are covered.
+    }
+    // This is to avoid undefined behavior if 'device' isn't a valid enum value.
+    return false;
+}
+
+static inline bool isOutputDevice(const std::string& device) {
+    return isOutputDevice(stringToAudioDevice(device));
+}
+
+static inline bool isTelephonyDevice(AudioDevice device) {
+    return device == AudioDevice::AUDIO_DEVICE_OUT_TELEPHONY_TX ||
+           device == AudioDevice::AUDIO_DEVICE_IN_TELEPHONY_RX;
+}
+
+static inline bool isTelephonyDevice(const std::string& device) {
+    return isTelephonyDevice(stringToAudioDevice(device));
+}
+
+static inline bool maybeVendorExtension(const std::string& s) {
+    // Only checks whether the string starts with the "vendor prefix".
+    static const std::string vendorPrefix = "VX_";
+    return s.size() > vendorPrefix.size() && s.substr(0, vendorPrefix.size()) == vendorPrefix;
+}
+
+static inline bool isVendorExtension(const std::string& s) {
+    // 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) {
+    return stringToAudioChannelMask(mask) == AudioChannelMask::UNKNOWN;
+}
+
+static inline bool isUnknownAudioContentType(const std::string& contentType) {
+    return stringToAudioContentType(contentType) == AudioContentType::UNKNOWN;
+}
+
+static inline bool isUnknownAudioDevice(const std::string& device) {
+    return stringToAudioDevice(device) == AudioDevice::UNKNOWN && !isVendorExtension(device);
+}
+
+static inline bool isUnknownAudioFormat(const std::string& format) {
+    return stringToAudioFormat(format) == AudioFormat::UNKNOWN && !isVendorExtension(format);
+}
+
+static inline bool isUnknownAudioGainMode(const std::string& mode) {
+    return stringToAudioGainMode(mode) == AudioGainMode::UNKNOWN;
+}
+
+static inline bool isUnknownAudioInOutFlag(const std::string& flag) {
+    return stringToAudioInOutFlag(flag) == AudioInOutFlag::UNKNOWN;
+}
+
+static inline bool isUnknownAudioSource(const std::string& source) {
+    return stringToAudioSource(source) == AudioSource::UNKNOWN;
+}
+
+static inline bool isUnknownAudioStreamType(const std::string& streamType) {
+    return stringToAudioStreamType(streamType) == AudioStreamType::UNKNOWN;
+}
+
+static inline bool isUnknownAudioUsage(const std::string& usage) {
+    return stringToAudioUsage(usage) == AudioUsage::UNKNOWN;
+}
+
+static inline bool isLinearPcm(AudioFormat format) {
+    switch (format) {
+        case AudioFormat::AUDIO_FORMAT_PCM_16_BIT:
+        case AudioFormat::AUDIO_FORMAT_PCM_8_BIT:
+        case AudioFormat::AUDIO_FORMAT_PCM_32_BIT:
+        case AudioFormat::AUDIO_FORMAT_PCM_8_24_BIT:
+        case AudioFormat::AUDIO_FORMAT_PCM_FLOAT:
+        case AudioFormat::AUDIO_FORMAT_PCM_24_BIT_PACKED:
+            return true;
+        default:
+            return false;
+    }
+}
+
+static inline bool isLinearPcm(const std::string& format) {
+    return isLinearPcm(stringToAudioFormat(format));
+}
+
+static inline bool isUnknownAudioEncapsulationType(const std::string& encapsulationType) {
+    return stringToAudioEncapsulationType(encapsulationType) == AudioEncapsulationType::UNKNOWN;
+}
+
+}  // namespace android::audio::policy::configuration::V7_0
+
+#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
new file mode 100644
index 0000000..a85e4fa
--- /dev/null
+++ b/audio/common/7.0/example/Android.bp
@@ -0,0 +1,53 @@
+//
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.audio@7.0-service.example",
+    vendor: true,
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.audio@7.0-service.example.rc"],
+    vintf_fragments: ["android.hardware.audio@7.0-service.example.xml"],
+    srcs: [
+        "DevicesFactory.cpp",
+        "Effect.cpp",
+        "EffectsFactory.cpp",
+        "EqualizerEffect.cpp",
+        "LoudnessEnhancerEffect.cpp",
+        "service.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    shared_libs: [
+        "libcutils",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+        "android.hardware.audio@7.0",
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.effect@7.0",
+    ],
+}
diff --git a/audio/common/7.0/example/DevicesFactory.cpp b/audio/common/7.0/example/DevicesFactory.cpp
new file mode 100644
index 0000000..ddd5fef
--- /dev/null
+++ b/audio/common/7.0/example/DevicesFactory.cpp
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DevicesFactory7.0"
+#include <log/log.h>
+
+#include "DevicesFactory.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+namespace android::hardware::audio::V7_0::implementation {
+
+Return<void> DevicesFactory::openDevice(const hidl_string& device, openDevice_cb _hidl_cb) {
+    (void)device;
+    _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
+    return Void();
+}
+
+Return<void> DevicesFactory::openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) {
+    _hidl_cb(Result::INVALID_ARGUMENTS, nullptr);
+    return Void();
+}
+
+}  // namespace android::hardware::audio::V7_0::implementation
diff --git a/audio/common/7.0/example/DevicesFactory.h b/audio/common/7.0/example/DevicesFactory.h
new file mode 100644
index 0000000..00f665c
--- /dev/null
+++ b/audio/common/7.0/example/DevicesFactory.h
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/audio/7.0/IDevicesFactory.h>
+
+namespace android::hardware::audio::V7_0::implementation {
+
+class DevicesFactory : public IDevicesFactory {
+  public:
+    DevicesFactory() = default;
+
+    ::android::hardware::Return<void> openDevice(const ::android::hardware::hidl_string& device,
+                                                 openDevice_cb _hidl_cb) override;
+
+    ::android::hardware::Return<void> openPrimaryDevice(openPrimaryDevice_cb _hidl_cb) override;
+};
+
+}  // namespace android::hardware::audio::V7_0::implementation
diff --git a/audio/common/7.0/example/Effect.cpp b/audio/common/7.0/example/Effect.cpp
new file mode 100644
index 0000000..0621669
--- /dev/null
+++ b/audio/common/7.0/example/Effect.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "EffectsFactory7.0"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+
+#include "Effect.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V7_0;
+// Make an alias for enumerations generated from the APM config XSD.
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+Return<Result> Effect::init() {
+    return Result::OK;
+}
+
+Return<Result> Effect::setConfig(
+        const EffectConfig& config,
+        const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+        const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) {
+    (void)config;
+    (void)inputBufferProvider;
+    (void)outputBufferProvider;
+    return Result::OK;
+}
+
+Return<Result> Effect::reset() {
+    return Result::OK;
+}
+
+Return<Result> Effect::enable() {
+    if (!mEnabled) {
+        mEnabled = true;
+        return Result::OK;
+    } else {
+        return Result::NOT_SUPPORTED;
+    }
+}
+
+Return<Result> Effect::disable() {
+    if (mEnabled) {
+        mEnabled = false;
+        return Result::OK;
+    } else {
+        return Result::NOT_SUPPORTED;
+    }
+}
+
+Return<Result> Effect::setDevice(const DeviceAddress& device) {
+    (void)device;
+    return Result::OK;
+}
+
+Return<void> Effect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
+                                     setAndGetVolume_cb _hidl_cb) {
+    (void)volumes;
+    _hidl_cb(Result::OK, hidl_vec<uint32_t>{});
+    return Void();
+}
+
+Return<Result> Effect::volumeChangeNotification(const hidl_vec<uint32_t>& volumes) {
+    (void)volumes;
+    return Result::OK;
+}
+
+Return<Result> Effect::setAudioMode(AudioMode mode) {
+    (void)mode;
+    return Result::OK;
+}
+
+Return<Result> Effect::setConfigReverse(
+        const EffectConfig& config,
+        const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+        const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) {
+    (void)config;
+    (void)inputBufferProvider;
+    (void)outputBufferProvider;
+    return Result::OK;
+}
+
+Return<Result> Effect::setInputDevice(const DeviceAddress& device) {
+    (void)device;
+    return Result::OK;
+}
+
+Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
+    EffectConfig config;
+    // inputCfg left unspecified.
+    config.outputCfg.base.format.value(toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT));
+    config.outputCfg.base.sampleRateHz.value(48000);
+    config.outputCfg.base.channelMask.value(
+            toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO));
+    config.outputCfg.accessMode.value(EffectBufferAccess::ACCESS_ACCUMULATE);
+    _hidl_cb(Result::OK, config);
+    return Void();
+}
+
+Return<void> Effect::getConfigReverse(getConfigReverse_cb _hidl_cb) {
+    _hidl_cb(Result::OK, EffectConfig{});
+    return Void();
+}
+
+Return<void> Effect::getSupportedAuxChannelsConfigs(uint32_t maxConfigs,
+                                                    getSupportedAuxChannelsConfigs_cb _hidl_cb) {
+    (void)maxConfigs;
+    _hidl_cb(Result::OK, hidl_vec<EffectAuxChannelsConfig>{});
+    return Void();
+}
+
+Return<void> Effect::getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) {
+    _hidl_cb(Result::OK, EffectAuxChannelsConfig{});
+    return Void();
+}
+
+Return<Result> Effect::setAuxChannelsConfig(const EffectAuxChannelsConfig& config) {
+    (void)config;
+    return Result::OK;
+}
+
+Return<Result> Effect::setAudioSource(const hidl_string& source) {
+    (void)source;
+    return Result::OK;
+}
+
+Return<Result> Effect::offload(const EffectOffloadParameter& param) {
+    (void)param;
+    return Result::OK;
+}
+
+Return<void> Effect::getDescriptor(getDescriptor_cb _hidl_cb) {
+    _hidl_cb(Result::OK, mDescriptor);
+    return Void();
+}
+
+Return<void> Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) {
+    _hidl_cb(Result::OK, MQDescriptor<Result, kSynchronizedReadWrite>{});
+    return Void();
+}
+
+Return<Result> Effect::setProcessBuffers(const AudioBuffer& inBuffer,
+                                         const AudioBuffer& outBuffer) {
+    (void)inBuffer;
+    (void)outBuffer;
+    return Result::OK;
+}
+
+Return<void> Effect::command(uint32_t commandId, const hidl_vec<uint8_t>& data,
+                             uint32_t resultMaxSize, command_cb _hidl_cb) {
+    (void)commandId;
+    (void)data;
+    (void)resultMaxSize;
+    _hidl_cb(-EINVAL, hidl_vec<uint8_t>{});
+    return Void();
+}
+
+Return<Result> Effect::setParameter(const hidl_vec<uint8_t>& parameter,
+                                    const hidl_vec<uint8_t>& value) {
+    (void)parameter;
+    (void)value;
+    return Result::OK;
+}
+
+Return<void> Effect::getParameter(const hidl_vec<uint8_t>& parameter, uint32_t valueMaxSize,
+                                  getParameter_cb _hidl_cb) {
+    (void)parameter;
+    (void)valueMaxSize;
+    _hidl_cb(Result::OK, hidl_vec<uint8_t>{});
+    return Void();
+}
+
+Return<void> Effect::getSupportedConfigsForFeature(uint32_t featureId, uint32_t maxConfigs,
+                                                   uint32_t configSize,
+                                                   getSupportedConfigsForFeature_cb _hidl_cb) {
+    (void)featureId;
+    (void)maxConfigs;
+    (void)configSize;
+    _hidl_cb(Result::OK, 0, hidl_vec<uint8_t>{});
+    return Void();
+}
+
+Return<void> Effect::getCurrentConfigForFeature(uint32_t featureId, uint32_t configSize,
+                                                getCurrentConfigForFeature_cb _hidl_cb) {
+    (void)featureId;
+    (void)configSize;
+    _hidl_cb(Result::OK, hidl_vec<uint8_t>{});
+    return Void();
+}
+
+Return<Result> Effect::setCurrentConfigForFeature(uint32_t featureId,
+                                                  const hidl_vec<uint8_t>& configData) {
+    (void)featureId;
+    (void)configData;
+    return Result::OK;
+}
+
+Return<Result> Effect::close() {
+    return Result::OK;
+}
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/Effect.h b/audio/common/7.0/example/Effect.h
new file mode 100644
index 0000000..fa7f41b
--- /dev/null
+++ b/audio/common/7.0/example/Effect.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.
+ */
+
+#pragma once
+
+#include <android/hardware/audio/effect/7.0/IEffect.h>
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+class Effect : public IEffect {
+  public:
+    explicit Effect(const EffectDescriptor& descriptor) : mDescriptor(descriptor) {}
+
+    ::android::hardware::Return<Result> init() override;
+    ::android::hardware::Return<Result> setConfig(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
+    ::android::hardware::Return<Result> reset() override;
+    ::android::hardware::Return<Result> enable() override;
+    ::android::hardware::Return<Result> disable() override;
+    ::android::hardware::Return<Result> setDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override;
+    ::android::hardware::Return<void> setAndGetVolume(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes,
+            setAndGetVolume_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> volumeChangeNotification(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes) override;
+    ::android::hardware::Return<Result> setAudioMode(
+            ::android::hardware::audio::common::V7_0::AudioMode mode) override;
+    ::android::hardware::Return<Result> setConfigReverse(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
+    ::android::hardware::Return<Result> setInputDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override;
+    ::android::hardware::Return<void> getConfig(getConfig_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getSupportedAuxChannelsConfigs(
+            uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getAuxChannelsConfig(
+            getAuxChannelsConfig_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setAuxChannelsConfig(
+            const EffectAuxChannelsConfig& config) override;
+    ::android::hardware::Return<Result> setAudioSource(
+            const ::android::hardware::hidl_string& source) override;
+    ::android::hardware::Return<Result> offload(const EffectOffloadParameter& param) override;
+    ::android::hardware::Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
+    ::android::hardware::Return<void> prepareForProcessing(
+            prepareForProcessing_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setProcessBuffers(const AudioBuffer& inBuffer,
+                                                          const AudioBuffer& outBuffer) override;
+    ::android::hardware::Return<void> command(uint32_t commandId,
+                                              const ::android::hardware::hidl_vec<uint8_t>& data,
+                                              uint32_t resultMaxSize, command_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter,
+            const ::android::hardware::hidl_vec<uint8_t>& value) override;
+    ::android::hardware::Return<void> getParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter, uint32_t valueMaxSize,
+            getParameter_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getSupportedConfigsForFeature(
+            uint32_t featureId, uint32_t maxConfigs, uint32_t configSize,
+            getSupportedConfigsForFeature_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getCurrentConfigForFeature(
+            uint32_t featureId, uint32_t configSize,
+            getCurrentConfigForFeature_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setCurrentConfigForFeature(
+            uint32_t featureId, const ::android::hardware::hidl_vec<uint8_t>& configData) override;
+    ::android::hardware::Return<Result> close() override;
+
+  private:
+    const EffectDescriptor mDescriptor;
+    bool mEnabled = false;
+};
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/EffectsFactory.cpp b/audio/common/7.0/example/EffectsFactory.cpp
new file mode 100644
index 0000000..7d333ae
--- /dev/null
+++ b/audio/common/7.0/example/EffectsFactory.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.
+ */
+
+#define LOG_TAG "EffectsFactory7.0"
+#include <log/log.h>
+
+#include "EffectsFactory.h"
+#include "EqualizerEffect.h"
+#include "LoudnessEnhancerEffect.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V7_0;
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+Return<void> EffectsFactory::getAllDescriptors(getAllDescriptors_cb _hidl_cb) {
+    hidl_vec<EffectDescriptor> descriptors;
+    descriptors.resize(2);
+    descriptors[0] = EqualizerEffect::getDescriptor();
+    descriptors[1] = LoudnessEnhancerEffect::getDescriptor();
+    _hidl_cb(Result::OK, descriptors);
+    return Void();
+}
+
+Return<void> EffectsFactory::getDescriptor(const Uuid& uuid, getDescriptor_cb _hidl_cb) {
+    if (auto desc = EqualizerEffect::getDescriptor(); uuid == desc.type || uuid == desc.uuid) {
+        _hidl_cb(Result::OK, desc);
+    } else if (auto desc = LoudnessEnhancerEffect::getDescriptor();
+               uuid == desc.type || uuid == desc.uuid) {
+        _hidl_cb(Result::OK, desc);
+    } else {
+        _hidl_cb(Result::INVALID_ARGUMENTS, EffectDescriptor{});
+    }
+    return Void();
+}
+
+Return<void> EffectsFactory::createEffect(const Uuid& uuid, int32_t session, int32_t ioHandle,
+                                          int32_t device, createEffect_cb _hidl_cb) {
+    (void)session;
+    (void)ioHandle;
+    (void)device;
+    if (auto desc = EqualizerEffect::getDescriptor(); uuid == desc.type || uuid == desc.uuid) {
+        _hidl_cb(Result::OK, new EqualizerEffect(), 0);
+    } else if (auto desc = LoudnessEnhancerEffect::getDescriptor();
+               uuid == desc.type || uuid == desc.uuid) {
+        _hidl_cb(Result::OK, new LoudnessEnhancerEffect(), 0);
+    } else {
+        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, 0);
+    }
+    return Void();
+}
+
+Return<void> EffectsFactory::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
+    (void)fd;
+    (void)options;
+    return Void();
+}
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/EffectsFactory.h b/audio/common/7.0/example/EffectsFactory.h
new file mode 100644
index 0000000..8fec70c
--- /dev/null
+++ b/audio/common/7.0/example/EffectsFactory.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/audio/effect/7.0/IEffectsFactory.h>
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+class EffectsFactory : public IEffectsFactory {
+  public:
+    EffectsFactory() = default;
+
+    ::android::hardware::Return<void> getAllDescriptors(getAllDescriptors_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getDescriptor(
+            const ::android::hardware::audio::common::V7_0::Uuid& uuid,
+            getDescriptor_cb _hidl_cb) override;
+    ::android::hardware::Return<void> createEffect(
+            const ::android::hardware::audio::common::V7_0::Uuid& uuid, int32_t session,
+            int32_t ioHandle, int32_t device, createEffect_cb _hidl_cb) override;
+    ::android::hardware::Return<void>
+    debug(const ::android::hardware::hidl_handle& fd,
+          const ::android::hardware::hidl_vec<::android::hardware::hidl_string>& options) override;
+};
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/EqualizerEffect.cpp b/audio/common/7.0/example/EqualizerEffect.cpp
new file mode 100644
index 0000000..c93c5a9
--- /dev/null
+++ b/audio/common/7.0/example/EqualizerEffect.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 <limits>
+
+#define LOG_TAG "EffectsFactory7.0"
+#include <log/log.h>
+
+#include "EqualizerEffect.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V7_0;
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+const EffectDescriptor& EqualizerEffect::getDescriptor() {
+    // Note: for VTS tests only 'type' and 'uuid' fields are required.
+    // The actual implementation must provide meaningful values
+    // for all fields of the descriptor.
+    static const EffectDescriptor descriptor = {
+            .type =
+                    {// Same UUID as AudioEffect.EFFECT_TYPE_EQUALIZER in Java.
+                     0x0bed4300, 0xddd6, 0x11db, 0x8f34,
+                     std::array<uint8_t, 6>{{0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}}},
+            .uuid = {0, 0, 0, 1, std::array<uint8_t, 6>{{0, 0, 0, 0, 0, 0}}}};
+    return descriptor;
+}
+
+EqualizerEffect::EqualizerEffect() : mEffect(new Effect(getDescriptor())) {
+    mProperties.bandLevels.resize(kNumBands);
+}
+
+Return<void> EqualizerEffect::getNumBands(getNumBands_cb _hidl_cb) {
+    _hidl_cb(Result::OK, kNumBands);
+    return Void();
+}
+
+Return<void> EqualizerEffect::getLevelRange(getLevelRange_cb _hidl_cb) {
+    _hidl_cb(Result::OK, std::numeric_limits<int16_t>::min(), std::numeric_limits<int16_t>::max());
+    return Void();
+}
+
+Return<Result> EqualizerEffect::setBandLevel(uint16_t band, int16_t level) {
+    if (band < kNumBands) {
+        mProperties.bandLevels[band] = level;
+        return Result::OK;
+    } else {
+        return Result::INVALID_ARGUMENTS;
+    }
+}
+
+Return<void> EqualizerEffect::getBandLevel(uint16_t band, getBandLevel_cb _hidl_cb) {
+    if (band < kNumBands) {
+        _hidl_cb(Result::OK, mProperties.bandLevels[band]);
+    } else {
+        _hidl_cb(Result::INVALID_ARGUMENTS, 0);
+    }
+    return Void();
+}
+
+Return<void> EqualizerEffect::getBandCenterFrequency(uint16_t band,
+                                                     getBandCenterFrequency_cb _hidl_cb) {
+    (void)band;
+    _hidl_cb(Result::OK, 0);
+    return Void();
+}
+
+Return<void> EqualizerEffect::getBandFrequencyRange(uint16_t band,
+                                                    getBandFrequencyRange_cb _hidl_cb) {
+    (void)band;
+    _hidl_cb(Result::OK, 0, 1);
+    return Void();
+}
+
+Return<void> EqualizerEffect::getBandForFrequency(uint32_t freq, getBandForFrequency_cb _hidl_cb) {
+    (void)freq;
+    _hidl_cb(Result::OK, 0);
+    return Void();
+}
+
+Return<void> EqualizerEffect::getPresetNames(getPresetNames_cb _hidl_cb) {
+    hidl_vec<hidl_string> presetNames;
+    presetNames.resize(kNumPresets);
+    presetNames[0] = "default";
+    _hidl_cb(Result::OK, presetNames);
+    return Void();
+}
+
+Return<Result> EqualizerEffect::setCurrentPreset(uint16_t preset) {
+    if (preset < kNumPresets) {
+        mProperties.curPreset = preset;
+        return Result::OK;
+    } else {
+        return Result::INVALID_ARGUMENTS;
+    }
+}
+
+Return<void> EqualizerEffect::getCurrentPreset(getCurrentPreset_cb _hidl_cb) {
+    _hidl_cb(Result::OK, mProperties.curPreset);
+    return Void();
+}
+
+Return<Result> EqualizerEffect::setAllProperties(
+        const IEqualizerEffect::AllProperties& properties) {
+    mProperties = properties;
+    return Result::OK;
+}
+
+Return<void> EqualizerEffect::getAllProperties(getAllProperties_cb _hidl_cb) {
+    _hidl_cb(Result::OK, mProperties);
+    return Void();
+}
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/EqualizerEffect.h b/audio/common/7.0/example/EqualizerEffect.h
new file mode 100644
index 0000000..11853c3
--- /dev/null
+++ b/audio/common/7.0/example/EqualizerEffect.h
@@ -0,0 +1,163 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/audio/effect/7.0/IEqualizerEffect.h>
+
+#include "Effect.h"
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+class EqualizerEffect : public IEqualizerEffect {
+  public:
+    static const EffectDescriptor& getDescriptor();
+
+    EqualizerEffect();
+
+    // Methods from IEffect interface.
+    ::android::hardware::Return<Result> init() override { return mEffect->init(); }
+    ::android::hardware::Return<Result> setConfig(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override {
+        return mEffect->setConfig(config, inputBufferProvider, outputBufferProvider);
+    }
+    ::android::hardware::Return<Result> reset() override { return mEffect->reset(); }
+    ::android::hardware::Return<Result> enable() override { return mEffect->enable(); }
+    ::android::hardware::Return<Result> disable() override { return mEffect->disable(); }
+    ::android::hardware::Return<Result> setDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override {
+        return mEffect->setDevice(device);
+    }
+    ::android::hardware::Return<void> setAndGetVolume(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes,
+            setAndGetVolume_cb _hidl_cb) override {
+        return mEffect->setAndGetVolume(volumes, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> volumeChangeNotification(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes) override {
+        return mEffect->volumeChangeNotification(volumes);
+    }
+    ::android::hardware::Return<Result> setAudioMode(
+            ::android::hardware::audio::common::V7_0::AudioMode mode) override {
+        return mEffect->setAudioMode(mode);
+    }
+    ::android::hardware::Return<Result> setConfigReverse(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override {
+        return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
+    }
+    ::android::hardware::Return<Result> setInputDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override {
+        return mEffect->setInputDevice(device);
+    }
+    ::android::hardware::Return<void> getConfig(getConfig_cb _hidl_cb) override {
+        return mEffect->getConfig(_hidl_cb);
+    }
+    ::android::hardware::Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override {
+        return mEffect->getConfigReverse(_hidl_cb);
+    }
+    ::android::hardware::Return<void> getSupportedAuxChannelsConfigs(
+            uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override {
+        return mEffect->getSupportedAuxChannelsConfigs(maxConfigs, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getAuxChannelsConfig(
+            getAuxChannelsConfig_cb _hidl_cb) override {
+        return mEffect->getAuxChannelsConfig(_hidl_cb);
+    }
+    ::android::hardware::Return<Result> setAuxChannelsConfig(
+            const EffectAuxChannelsConfig& config) override {
+        return mEffect->setAuxChannelsConfig(config);
+    }
+    ::android::hardware::Return<Result> setAudioSource(
+            const ::android::hardware::hidl_string& source) override {
+        return mEffect->setAudioSource(source);
+    }
+    ::android::hardware::Return<Result> offload(const EffectOffloadParameter& param) override {
+        return mEffect->offload(param);
+    }
+    ::android::hardware::Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override {
+        return mEffect->getDescriptor(_hidl_cb);
+    }
+    ::android::hardware::Return<void> prepareForProcessing(
+            prepareForProcessing_cb _hidl_cb) override {
+        return mEffect->prepareForProcessing(_hidl_cb);
+    }
+    ::android::hardware::Return<Result> setProcessBuffers(const AudioBuffer& inBuffer,
+                                                          const AudioBuffer& outBuffer) override {
+        return mEffect->setProcessBuffers(inBuffer, outBuffer);
+    }
+    ::android::hardware::Return<void> command(uint32_t commandId,
+                                              const ::android::hardware::hidl_vec<uint8_t>& data,
+                                              uint32_t resultMaxSize,
+                                              command_cb _hidl_cb) override {
+        return mEffect->command(commandId, data, resultMaxSize, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> setParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter,
+            const ::android::hardware::hidl_vec<uint8_t>& value) override {
+        return mEffect->setParameter(parameter, value);
+    }
+    ::android::hardware::Return<void> getParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter, uint32_t valueMaxSize,
+            getParameter_cb _hidl_cb) override {
+        return mEffect->getParameter(parameter, valueMaxSize, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getSupportedConfigsForFeature(
+            uint32_t featureId, uint32_t maxConfigs, uint32_t configSize,
+            getSupportedConfigsForFeature_cb _hidl_cb) override {
+        return mEffect->getSupportedConfigsForFeature(featureId, maxConfigs, configSize, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getCurrentConfigForFeature(
+            uint32_t featureId, uint32_t configSize,
+            getCurrentConfigForFeature_cb _hidl_cb) override {
+        return mEffect->getCurrentConfigForFeature(featureId, configSize, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> setCurrentConfigForFeature(
+            uint32_t featureId, const ::android::hardware::hidl_vec<uint8_t>& configData) override {
+        return mEffect->setCurrentConfigForFeature(featureId, configData);
+    }
+    ::android::hardware::Return<Result> close() override { return mEffect->close(); }
+
+    // Methods from IEqualizerEffect interface.
+    ::android::hardware::Return<void> getNumBands(getNumBands_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getLevelRange(getLevelRange_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setBandLevel(uint16_t band, int16_t level) override;
+    ::android::hardware::Return<void> getBandLevel(uint16_t band,
+                                                   getBandLevel_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getBandCenterFrequency(
+            uint16_t band, getBandCenterFrequency_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getBandFrequencyRange(
+            uint16_t band, getBandFrequencyRange_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getBandForFrequency(uint32_t freq,
+                                                          getBandForFrequency_cb _hidl_cb) override;
+    ::android::hardware::Return<void> getPresetNames(getPresetNames_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setCurrentPreset(uint16_t preset) override;
+    ::android::hardware::Return<void> getCurrentPreset(getCurrentPreset_cb _hidl_cb) override;
+    ::android::hardware::Return<Result> setAllProperties(
+            const IEqualizerEffect::AllProperties& properties) override;
+    ::android::hardware::Return<void> getAllProperties(getAllProperties_cb _hidl_cb) override;
+
+  private:
+    static constexpr size_t kNumBands = 1;
+    static constexpr size_t kNumPresets = 1;
+    sp<Effect> mEffect;
+    IEqualizerEffect::AllProperties mProperties{};
+};
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/LoudnessEnhancerEffect.cpp b/audio/common/7.0/example/LoudnessEnhancerEffect.cpp
new file mode 100644
index 0000000..38269b3
--- /dev/null
+++ b/audio/common/7.0/example/LoudnessEnhancerEffect.cpp
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "EffectsFactory7.0"
+#include <log/log.h>
+
+#include "LoudnessEnhancerEffect.h"
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using namespace ::android::hardware::audio::common::V7_0;
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+const EffectDescriptor& LoudnessEnhancerEffect::getDescriptor() {
+    // Note: for VTS tests only 'type' and 'uuid' fields are required.
+    // The actual implementation must provide meaningful values
+    // for all fields of the descriptor.
+    static const EffectDescriptor descriptor = {
+            .type =
+                    {// Same UUID as AudioEffect.EFFECT_TYPE_LOUDNESS_ENHANCER in Java.
+                     0xfe3199be, 0xaed0, 0x413f, 0x87bb,
+                     std::array<uint8_t, 6>{{0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}},
+            .uuid = {0, 0, 0, 2, std::array<uint8_t, 6>{{0, 0, 0, 0, 0, 0}}}};
+    return descriptor;
+}  // namespace android::hardware::audio::effect::V7_0::implementation
+
+LoudnessEnhancerEffect::LoudnessEnhancerEffect() : mEffect(new Effect(getDescriptor())) {}
+
+Return<Result> LoudnessEnhancerEffect::setTargetGain(int32_t targetGainMb) {
+    mTargetGainMb = targetGainMb;
+    return Result::OK;
+}
+
+Return<void> LoudnessEnhancerEffect::getTargetGain(getTargetGain_cb _hidl_cb) {
+    _hidl_cb(Result::OK, mTargetGainMb);
+    return Void();
+}
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/LoudnessEnhancerEffect.h b/audio/common/7.0/example/LoudnessEnhancerEffect.h
new file mode 100644
index 0000000..1af0d9f
--- /dev/null
+++ b/audio/common/7.0/example/LoudnessEnhancerEffect.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/audio/effect/7.0/ILoudnessEnhancerEffect.h>
+
+#include "Effect.h"
+
+namespace android::hardware::audio::effect::V7_0::implementation {
+
+class LoudnessEnhancerEffect : public ILoudnessEnhancerEffect {
+  public:
+    static const EffectDescriptor& getDescriptor();
+
+    LoudnessEnhancerEffect();
+
+    // Methods from IEffect interface.
+    ::android::hardware::Return<Result> init() override { return mEffect->init(); }
+    ::android::hardware::Return<Result> setConfig(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override {
+        return mEffect->setConfig(config, inputBufferProvider, outputBufferProvider);
+    }
+    ::android::hardware::Return<Result> reset() override { return mEffect->reset(); }
+    ::android::hardware::Return<Result> enable() override { return mEffect->enable(); }
+    ::android::hardware::Return<Result> disable() override { return mEffect->disable(); }
+    ::android::hardware::Return<Result> setDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override {
+        return mEffect->setDevice(device);
+    }
+    ::android::hardware::Return<void> setAndGetVolume(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes,
+            setAndGetVolume_cb _hidl_cb) override {
+        return mEffect->setAndGetVolume(volumes, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> volumeChangeNotification(
+            const ::android::hardware::hidl_vec<uint32_t>& volumes) override {
+        return mEffect->volumeChangeNotification(volumes);
+    }
+    ::android::hardware::Return<Result> setAudioMode(
+            ::android::hardware::audio::common::V7_0::AudioMode mode) override {
+        return mEffect->setAudioMode(mode);
+    }
+    ::android::hardware::Return<Result> setConfigReverse(
+            const EffectConfig& config,
+            const ::android::sp<IEffectBufferProviderCallback>& inputBufferProvider,
+            const ::android::sp<IEffectBufferProviderCallback>& outputBufferProvider) override {
+        return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
+    }
+    ::android::hardware::Return<Result> setInputDevice(
+            const ::android::hardware::audio::common::V7_0::DeviceAddress& device) override {
+        return mEffect->setInputDevice(device);
+    }
+    ::android::hardware::Return<void> getConfig(getConfig_cb _hidl_cb) override {
+        return mEffect->getConfig(_hidl_cb);
+    }
+    ::android::hardware::Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override {
+        return mEffect->getConfigReverse(_hidl_cb);
+    }
+    ::android::hardware::Return<void> getSupportedAuxChannelsConfigs(
+            uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override {
+        return mEffect->getSupportedAuxChannelsConfigs(maxConfigs, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getAuxChannelsConfig(
+            getAuxChannelsConfig_cb _hidl_cb) override {
+        return mEffect->getAuxChannelsConfig(_hidl_cb);
+    }
+    ::android::hardware::Return<Result> setAuxChannelsConfig(
+            const EffectAuxChannelsConfig& config) override {
+        return mEffect->setAuxChannelsConfig(config);
+    }
+    ::android::hardware::Return<Result> setAudioSource(
+            const ::android::hardware::hidl_string& source) override {
+        return mEffect->setAudioSource(source);
+    }
+    ::android::hardware::Return<Result> offload(const EffectOffloadParameter& param) override {
+        return mEffect->offload(param);
+    }
+    ::android::hardware::Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override {
+        return mEffect->getDescriptor(_hidl_cb);
+    }
+    ::android::hardware::Return<void> prepareForProcessing(
+            prepareForProcessing_cb _hidl_cb) override {
+        return mEffect->prepareForProcessing(_hidl_cb);
+    }
+    ::android::hardware::Return<Result> setProcessBuffers(const AudioBuffer& inBuffer,
+                                                          const AudioBuffer& outBuffer) override {
+        return mEffect->setProcessBuffers(inBuffer, outBuffer);
+    }
+    ::android::hardware::Return<void> command(uint32_t commandId,
+                                              const ::android::hardware::hidl_vec<uint8_t>& data,
+                                              uint32_t resultMaxSize,
+                                              command_cb _hidl_cb) override {
+        return mEffect->command(commandId, data, resultMaxSize, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> setParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter,
+            const ::android::hardware::hidl_vec<uint8_t>& value) override {
+        return mEffect->setParameter(parameter, value);
+    }
+    ::android::hardware::Return<void> getParameter(
+            const ::android::hardware::hidl_vec<uint8_t>& parameter, uint32_t valueMaxSize,
+            getParameter_cb _hidl_cb) override {
+        return mEffect->getParameter(parameter, valueMaxSize, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getSupportedConfigsForFeature(
+            uint32_t featureId, uint32_t maxConfigs, uint32_t configSize,
+            getSupportedConfigsForFeature_cb _hidl_cb) override {
+        return mEffect->getSupportedConfigsForFeature(featureId, maxConfigs, configSize, _hidl_cb);
+    }
+    ::android::hardware::Return<void> getCurrentConfigForFeature(
+            uint32_t featureId, uint32_t configSize,
+            getCurrentConfigForFeature_cb _hidl_cb) override {
+        return mEffect->getCurrentConfigForFeature(featureId, configSize, _hidl_cb);
+    }
+    ::android::hardware::Return<Result> setCurrentConfigForFeature(
+            uint32_t featureId, const ::android::hardware::hidl_vec<uint8_t>& configData) override {
+        return mEffect->setCurrentConfigForFeature(featureId, configData);
+    }
+    ::android::hardware::Return<Result> close() override { return mEffect->close(); }
+
+    // Methods from ILoudnessEnhancerEffect interface.
+    ::android::hardware::Return<Result> setTargetGain(int32_t targetGainMb) override;
+    ::android::hardware::Return<void> getTargetGain(getTargetGain_cb _hidl_cb) override;
+
+  private:
+    sp<Effect> mEffect;
+    int32_t mTargetGainMb = 0;
+};
+
+}  // namespace android::hardware::audio::effect::V7_0::implementation
diff --git a/audio/common/7.0/example/OWNERS b/audio/common/7.0/example/OWNERS
new file mode 100644
index 0000000..24071af
--- /dev/null
+++ b/audio/common/7.0/example/OWNERS
@@ -0,0 +1,2 @@
+elaurent@google.com
+mnaganov@google.com
diff --git a/audio/common/7.0/example/android.hardware.audio@7.0-service.example.rc b/audio/common/7.0/example/android.hardware.audio@7.0-service.example.rc
new file mode 100644
index 0000000..cf8b51f
--- /dev/null
+++ b/audio/common/7.0/example/android.hardware.audio@7.0-service.example.rc
@@ -0,0 +1,7 @@
+service vendor.audio-hal-7-0 /vendor/bin/hw/android.hardware.audio@7.0-service.example
+    class hal
+    user audioserver
+    group audio
+    capabilities BLOCK_SUSPEND
+    ioprio rt 4
+    task_profiles ProcessCapacityHigh HighPerformance
diff --git a/audio/common/7.0/example/android.hardware.audio@7.0-service.example.xml b/audio/common/7.0/example/android.hardware.audio@7.0-service.example.xml
new file mode 100644
index 0000000..b91b061
--- /dev/null
+++ b/audio/common/7.0/example/android.hardware.audio@7.0-service.example.xml
@@ -0,0 +1,20 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.audio</name>
+        <transport>hwbinder</transport>
+        <version>7.0</version>
+        <interface>
+            <name>IDevicesFactory</name>
+            <instance>example</instance>
+        </interface>
+    </hal>
+    <hal format="hidl">
+        <name>android.hardware.audio.effect</name>
+        <transport>hwbinder</transport>
+        <version>7.0</version>
+        <interface>
+            <name>IEffectsFactory</name>
+            <instance>example</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/audio/common/7.0/example/service.cpp b/audio/common/7.0/example/service.cpp
new file mode 100644
index 0000000..641e2c9
--- /dev/null
+++ b/audio/common/7.0/example/service.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.audio@7.0-service.example"
+#include <hidl/HidlTransportSupport.h>
+#include <log/log.h>
+
+#include "DevicesFactory.h"
+#include "EffectsFactory.h"
+
+using android::hardware::configureRpcThreadpool;
+using android::hardware::joinRpcThreadpool;
+using namespace android;
+
+status_t registerDevicesFactoryService() {
+    sp<::android::hardware::audio::V7_0::IDevicesFactory> devicesFactory =
+            new ::android::hardware::audio::V7_0::implementation::DevicesFactory();
+    status_t status = devicesFactory->registerAsService("example");
+    ALOGE_IF(status != OK, "Error registering devices factory as service: %d", status);
+    return status;
+}
+
+status_t registerEffectsFactoryService() {
+    sp<::android::hardware::audio::effect::V7_0::IEffectsFactory> devicesFactory =
+            new ::android::hardware::audio::effect::V7_0::implementation::EffectsFactory();
+    status_t status = devicesFactory->registerAsService("example");
+    ALOGE_IF(status != OK, "Error registering effects factory as service: %d", status);
+    return status;
+}
+
+int main() {
+    configureRpcThreadpool(1, true);
+    status_t status = registerDevicesFactoryService();
+    if (status != OK) {
+        return status;
+    }
+    status = registerEffectsFactoryService();
+    if (status != OK) {
+        return status;
+    }
+    joinRpcThreadpool();
+
+    return 1;
+}
diff --git a/audio/common/7.0/types.hal b/audio/common/7.0/types.hal
new file mode 100644
index 0000000..6fca93e
--- /dev/null
+++ b/audio/common/7.0/types.hal
@@ -0,0 +1,543 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.common@7.0;
+
+import android.hidl.safe_union@1.0;
+
+/**
+ * Handle type for identifying audio resources. Handles are allocated by the framework.
+ */
+typedef int32_t AudioIoHandle;
+
+/**
+ * Each port has a unique ID or handle allocated by policy manager.
+ */
+typedef int32_t AudioPortHandle;
+
+/**
+ * Each patch is identified by a handle allocated by the HAL.
+ */
+typedef int32_t AudioPatchHandle;
+
+/**
+ * A HW synchronization source returned by the audio HAL.
+ */
+typedef uint32_t AudioHwSync;
+
+/**
+ * Commonly used structure for passing unique identifieds (UUID).
+ * For the definition of UUID, refer to ITU-T X.667 spec.
+ */
+struct Uuid {
+    uint32_t timeLow;
+    uint16_t timeMid;
+    uint16_t versionAndTimeHigh;
+    uint16_t variantAndClockSeqHigh;
+    uint8_t[6] node;
+};
+
+
+/*
+ *
+ *  Audio streams
+ *
+ */
+
+/**
+ * Audio stream type describing the intended use case of a stream.
+ * See 'audioStreamType' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ *
+ * An empty string is used to specify the "default" stream type.
+ */
+typedef string AudioStreamType;
+
+/**
+ * An audio source defines the intended use case for the sound being recorded.
+ * See 'audioSource' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioSource;
+
+/**
+ * An audio session identifier is used to designate the particular
+ * playback or recording session (e.g. playback performed by a certain
+ * application).
+ */
+typedef int32_t AudioSession;
+
+@export(name="", value_prefix="HAL_AUDIO_SESSION_")
+enum AudioSessionConsts : int32_t {
+    /**
+     * Session for effects attached to a particular sink or source audio device
+     * (e.g an effect only applied to a speaker)
+     */
+    DEVICE = -2,
+    /**
+     * Session for effects attached to a particular output stream
+     * (value must be less than 0)
+     */
+    OUTPUT_STAGE = -1,
+    /**
+     * Session for effects applied to output mix. These effects can
+     * be moved by audio policy manager to another output stream
+     * (value must be 0)
+     */
+    OUTPUT_MIX = 0,
+};
+
+/**
+ * Audio format indicates audio codec type.
+ * See 'audioFormat' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioFormat;
+
+/**
+ * Audio channel mask indicates presence of particular channels.
+ * Note that every value is already a mask comprised of individual
+ * channels.
+ * See 'audioChannelMask' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioChannelMask;
+
+/**
+ * Base configuration attributes applicable to any stream of audio.
+ */
+struct AudioConfigBase {
+    AudioFormat format;
+    uint32_t sampleRateHz;
+    AudioChannelMask channelMask;
+};
+
+/**
+ * Base configuration attributes applicable to any stream of audio.
+ * Any attribute may be left unspecified.
+ */
+struct AudioConfigBaseOptional {
+    safe_union Format {
+        Monostate unspecified;
+        AudioFormat value;
+    } format;
+    safe_union SampleRate {
+        Monostate unspecified;
+        uint32_t value;
+    } sampleRateHz;
+    safe_union ChannelMask {
+        Monostate unspecified;
+        AudioChannelMask value;
+    } channelMask;
+};
+
+/**
+ * Audio encapsulation type indicates the encapsulation type that is required
+ * for playback/capture.
+ * See 'audioEncapsulationType' in audio_policy_configuration.xsd for the list
+ * of allowed values.
+ */
+typedef string AudioEncapsulationType;
+
+/**
+ * Configurations supported for a certain audio format.
+ */
+struct AudioProfile {
+    AudioFormat format;
+    /** List of the sample rates (in Hz) supported by the profile. */
+    vec<uint32_t> sampleRates;
+    /** List of channel masks supported by the profile. */
+    vec<AudioChannelMask> channelMasks;
+};
+
+/**
+ * AudioTransport struct describes the capability of an audio port. The
+ * capability is described via AudioProfile or raw hardware descriptors for
+ * for formats that are not supported by the platform.
+ */
+struct AudioTransport {
+    safe_union AudioCapability {
+        /**
+         * A certain audio format that is known by the platform and its
+         * corresponding configuration.
+         */
+        AudioProfile profile;
+        /**
+         * The audio descriptor that is reported from EDID. See HDMI
+         * specification 1.4b section 7 and CEA-861-G section 7.5.2 for more
+         * information. When this value is set, it indicates the standard is
+         * AUDIO_STANDARD_EDID.
+         */
+        vec<uint8_t> edid;
+    } audioCapability;
+
+    /**
+     * The encapsulation type that is required when the framework is using this
+     * format when playing or capturing to/from a stream or device exposing this
+     * audio transport.
+     */
+    AudioEncapsulationType encapsulationType;
+};
+
+/**
+ * Major modes for a mobile device. The current mode setting affects audio
+ * routing.
+ */
+@export(name="", value_prefix="HAL_AUDIO_MODE_")
+enum AudioMode : int32_t {
+    NORMAL           = 0,
+    RINGTONE         = 1,
+    /** Calls handled by the telephony stack (Eg: PSTN). */
+    IN_CALL          = 2,
+    /** Calls handled by apps (Eg: Hangout). */
+    IN_COMMUNICATION = 3,
+    /** Call screening in progress. */
+    CALL_SCREEN      = 4,
+};
+
+/**
+ * Audio device specifies type (or category) of audio I/O device
+ * (e.g. speaker or headphones).
+ * See 'audioDevice' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioDevice;
+
+/**
+ * Specifies a device address in case when several devices of the same type
+ * can be connected (e.g. BT A2DP, USB).
+ */
+struct DeviceAddress {
+    /** The type of the device. */
+    AudioDevice deviceType;
+    safe_union Address {
+        /**
+         * String uniquely identifying the device among other devices
+         * of the same type. Can be empty in case there is only one device
+         * of this type.
+         *
+         * Depending on the device type, its id may be assigned by the framework
+         * (this is done for REMOTE_SUBMIX), or specified in the audio policy
+         * configuration file (typically done for BUS devices), or assigned
+         * by the HAL service. In any case, both framework and HAL must
+         * never attempt to parse the value of the id. If the address must
+         * be parsed, one of the members below must be used instead of 'id'.
+         */
+        string id;
+        /** IEEE 802 MAC address. Set for Bluetooth devices. */
+        uint8_t[6] mac;
+        /** IPv4 Address. Set for IPv4 devices. */
+        uint8_t[4] ipv4;
+        /** IPv6 Address. Set for IPv6 devices. */
+        uint16_t[8] ipv6;
+        /** PCI bus Address. Set for USB devices. */
+        struct Alsa {
+            int32_t card;
+            int32_t device;
+        } alsa;
+    } address;
+};
+
+/**
+ * Audio usage specifies the intended use case for the sound being played.
+ * See 'audioUsage' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioUsage;
+
+/**
+ * Audio content type expresses the general category of the content.
+ * See 'audioContentType' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioContentType;
+
+/** Encapsulation mode used for sending audio compressed data. */
+@export(name="audio_encapsulation_mode_t", value_prefix="AUDIO_ENCAPSULATION_MODE_")
+enum AudioEncapsulationMode : int32_t {
+    // Do not change these values without updating their counterparts
+    // in frameworks/base/media/java/android/media/AudioTrack.java
+    /**
+     * No encapsulation mode for metadata.
+     */
+    NONE              = 0,
+    /**
+     * Elementary stream payload with metadata
+     */
+    ELEMENTARY_STREAM = 1,
+    /**
+     *  Handle-based payload with metadata
+     */
+    HANDLE            = 2,
+};
+
+/**
+ * Enumeration of metadata types permitted for use by
+ * encapsulation mode audio streams.
+ */
+@export(name="audio_encapsulation_metadata_type_t", value_prefix="AUDIO_ENCAPSULATION_METADATA_TYPE_")
+enum AudioEncapsulationMetadataType : int32_t {
+    /**
+     * No metadata.
+     */
+    NONE              = 0,
+    /**
+     * Encapsulation metadata type for framework tuner information.
+     */
+    FRAMEWORK_TUNER   = 1,
+    /**
+     * Encapsulation metadata type for DVB AD descriptor.
+     * This metadata is formatted per ETSI TS 101 154 Table E.1: AD_descriptor.
+     */
+    DVB_AD_DESCRIPTOR = 2,
+};
+
+/**
+ * Additional information about the stream passed to hardware decoders.
+ */
+struct AudioOffloadInfo {
+    AudioConfigBase base;
+    AudioStreamType streamType;
+    uint32_t bitRatePerSecond;
+    int64_t durationMicroseconds;  // -1 if unknown
+    bool hasVideo;
+    bool isStreaming;
+    uint32_t bitWidth;
+    uint32_t bufferSize;
+    AudioUsage usage;
+    AudioEncapsulationMode encapsulationMode;
+    int32_t contentId;
+    int32_t syncId;
+};
+
+/**
+ * Commonly used audio stream configuration parameters.
+ */
+struct AudioConfig {
+    AudioConfigBase base;
+    safe_union OffloadInfo {
+        Monostate unspecified;
+        AudioOffloadInfo info;
+    } offloadInfo;
+    uint64_t frameCount;
+};
+
+/**
+ * AudioTag is an additional use case qualifier complementing
+ * AudioUsage and AudioContentType. Tags are set by vendor specific applications
+ * and must be prefixed by "VX_". Vendor must namespace their tag
+ * names to avoid conflicts. See 'vendorExtension' in audio_policy_configuration.xsd
+ * for a formal definition.
+ */
+typedef string AudioTag;
+
+/** Metadata of a playback track for a StreamOut. */
+struct PlaybackTrackMetadata {
+    AudioUsage usage;
+    AudioContentType contentType;
+    /**
+     * Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
+     * 2 means double amplification...
+     * Must not be negative.
+     */
+    float gain;
+    AudioChannelMask channelMask;
+    /** Tags from AudioTrack audio atttributes */
+    vec<AudioTag> tags;
+};
+
+/** Metadatas of the source of a StreamOut. */
+struct SourceMetadata {
+    vec<PlaybackTrackMetadata> tracks;
+};
+
+/** Metadata of a record track for a StreamIn. */
+struct RecordTrackMetadata {
+    AudioSource source;
+    /**
+     * Positive linear gain applied to the track samples. 0 being muted and 1 is no attenuation,
+     * 2 means double amplification...
+     * Must not be negative.
+     */
+    float gain;
+    /**
+     * Indicates the destination of an input stream, can be left unspecified.
+     */
+    safe_union Destination {
+        Monostate unspecified;
+        DeviceAddress device;
+    } destination;
+    AudioChannelMask channelMask;
+    /** Tags from AudioRecord audio atttributes */
+    vec<AudioTag> tags;
+};
+
+/** Metadatas of the sink of a StreamIn. */
+struct SinkMetadata {
+    vec<RecordTrackMetadata> tracks;
+};
+
+/*
+ *
+ *  Volume control
+ *
+ */
+
+/**
+ * Type of gain control exposed by an audio port.
+ * See 'audioGainMode' in audio_policy_configuration.xsd for the
+ * list of allowed values.
+ */
+typedef string AudioGainMode;
+
+/**
+ * An audio_gain struct is a representation of a gain stage.
+ * A gain stage is always attached to an audio port.
+ */
+struct AudioGain {
+    /** Modes of operation. */
+    vec<AudioGainMode> mode;
+    /** Channels which gain can be controlled. */
+    AudioChannelMask channelMask;
+    /** Minimum gain value in millibels. */
+    int32_t minValue;
+    /** Maximum gain value in millibels. */
+    int32_t maxValue;
+    /** Default gain value in millibels. */
+    int32_t defaultValue;
+    /** Gain step in millibels. */
+    uint32_t stepValue;
+    /** Ramp duration in ms. */
+    uint32_t minRampMs;
+    /** Maximum ramp duration in ms. */
+    uint32_t maxRampMs;
+};
+
+/**
+ * The gain configuration structure is used to get or set the gain values of a
+ * given port.
+ */
+struct AudioGainConfig {
+    /** Index of the corresponding AudioGain in AudioPort.gains. */
+    int32_t index;
+    /** Modes of operation. */
+    vec<AudioGainMode> mode;
+    /** Channels which gain value follows. */
+    AudioChannelMask channelMask;
+    /**
+     * Gain values in millibels for each channel ordered from LSb to MSb in
+     * channel mask. The number of values is 1 in joint mode or
+     * the number of channels in the channel mask.
+     */
+    vec<int32_t> values;
+    /** Ramp duration in ms. */
+    uint32_t rampDurationMs;
+};
+
+
+/*
+ *
+ *  Routing control
+ *
+ */
+
+/*
+ * Types defined here are used to describe an audio source or sink at internal
+ * framework interfaces (audio policy, patch panel) or at the audio HAL.
+ * Sink and sources are grouped in a concept of “audio port” representing an
+ * audio end point at the edge of the system managed by the module exposing
+ * the interface.
+ */
+
+/**
+ * A helper aggregate structure providing parameters that depend on the
+ * port role.
+ */
+safe_union AudioPortExtendedInfo {
+    /** Set when no information is provided. */
+    Monostate unspecified;
+    /** Set when the audio port is an audio device. */
+    DeviceAddress device;
+    /** Set when the audio port is a mix. The handle is of a stream. */
+    struct AudioPortMixExt {
+        /** I/O handle of the input/output stream. */
+        AudioIoHandle ioHandle;
+        safe_union UseCase {
+            /** Specified when the port is in the SOURCE role. */
+            AudioStreamType stream;
+            /** Specified when the port is in the SINK role. */
+            AudioSource source;
+        } useCase;
+    } mix;
+    /** Set when the audio port is an audio session. */
+    AudioSession session;
+};
+
+/**
+ * Audio port configuration structure used to specify a particular configuration
+ * of an audio port.
+ */
+struct AudioPortConfig {
+    /**
+     * The 'id' field is set when it is needed to select the port and
+     * apply new configuration for it.
+     */
+    AudioPortHandle id;
+    /**
+     * Basic parameters: sampling rate, format, channel mask.
+     */
+    AudioConfigBaseOptional base;
+    /** Associated gain control. */
+    safe_union OptionalGain {
+        Monostate unspecified;
+        AudioGainConfig config;
+    } gain;
+    /** Parameters that depend on the actual port role. */
+    AudioPortExtendedInfo ext;
+};
+
+/**
+ * Audio port structure describes the capabilities of an audio port
+ * as well as its current configuration.
+ */
+struct AudioPort {
+    /**
+     * Unique identifier of the port within this HAL service. When calling
+     * from the client side functions like IDevice.getAudioPort is it allowed
+     * to only specify the 'id' and leave the other fields unspecified.
+     */
+    AudioPortHandle id;
+    /**
+     * Human-readable name describing the function of the port.
+     * E.g. "telephony_tx" or "fm_tuner".
+     */
+    string name;
+    /**
+     * List of audio transports supported by the audio port. This includes
+     * supported formats and raw hardware descriptors for formats not supported
+     * by the platform.
+     */
+    vec<AudioTransport> transports;
+    /** List of gain controls attached to the port. */
+    vec<AudioGain> gains;
+    /** Parameters that depend on the actual port role. */
+    AudioPortExtendedInfo ext;
+    /**
+     * Current configuration of the audio port, may have all the fields left
+     * unspecified.
+     */
+    AudioPortConfig activeConfig;
+};
diff --git a/audio/common/all-versions/OWNERS b/audio/common/all-versions/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/common/all-versions/OWNERS
+++ b/audio/common/all-versions/OWNERS
@@ -1,3 +1,2 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
diff --git a/audio/common/all-versions/copyHAL.sh b/audio/common/all-versions/copyHAL.sh
index 0a32a51..23e057a 100755
--- a/audio/common/all-versions/copyHAL.sh
+++ b/audio/common/all-versions/copyHAL.sh
@@ -16,6 +16,7 @@
 readonly HAL_DIRECTORY=hardware/interfaces/audio
 readonly HAL_VTS_DIRECTORY=core/all-versions/vts/functional
 readonly HAL_VTS_FILE=AudioPrimaryHidlHalTest.cpp
+readonly HAL_EFFECT_VTS_DIRECTORY=effect/all-versions/vts/functional
 readonly HAL_SERVICE_DIRECTORY=common/all-versions/default/service/
 readonly HAL_SERVICE_CPP=service.cpp
 
@@ -25,7 +26,7 @@
 
 readonly VTS_DIRECTORY=test/vts-testcase/hal/audio
 readonly VTS_LIST=test/vts/tools/build/tasks/list/vts_test_lib_hidl_package_list.mk
-readonly WATCHDOG=frameworks/base/services/core/java/com/android/server/Watchdog.cpp
+readonly WATCHDOG=frameworks/base/services/core/java/com/android/server/Watchdog.java
 readonly DUMP_UTILS=frameworks/native/libs/dumputils/dump_utils.cpp
 readonly GSI_CURRENT=build/make/target/product/gsi/current.txt
 
@@ -45,6 +46,9 @@
 readonly BASE_VERSION_ESCAPE="${BASE_MAJOR_VERSION}\.${BASE_MINOR_VERSION}"
 readonly BASE_VERSION_UNDERSCORE="${BASE_MAJOR_VERSION}_${BASE_MINOR_VERSION}"
 readonly NEW_VERSION_UNDERSCORE="${NEW_MAJOR_VERSION}_${NEW_MINOR_VERSION}"
+
+readonly HAL_VTS_CONFIG_FILE_GLOB="*Audio*V${BASE_VERSION_UNDERSCORE}*Test.xml"
+
 updateVersion() {
     if [ $1 == "-e" ]; then
         local -r REGEX="$2"; shift 2
@@ -71,6 +75,10 @@
     updateVersion -e "audio.*$BASE_VERSION_REGEX" "$@"
 }
 
+updateAudioVtsTargetVersion() {
+    updateVersion -e "Audio.*V$BASE_VERSION_REGEX" "$@"
+}
+
 updateLicenceDates() {
     # Update date on the 2 first lines
     sed -i "1,2 s/20[0-9][0-9]/$(date +"%Y")/g" "$@"
@@ -101,9 +109,16 @@
         cp -Tar $DIR/$BASE_VERSION $DIR/$NEW_VERSION
         COPY+=" $DIR/$NEW_VERSION"
     done
+    local COPY_FILES_TO=
+    for FILE_FROM in $(find . -type f -name "$HAL_VTS_CONFIG_FILE_GLOB"); do
+        local FILE_TO=${FILE_FROM/$BASE_VERSION_UNDERSCORE/$NEW_VERSION_UNDERSCORE}
+        cp "$FILE_FROM" "$FILE_TO"
+        COPY_FILES_TO+=" $FILE_TO"
+    done
 
     echo "Replacing $BASE_VERSION by $NEW_VERSION in the copied files"
     updateVersion $(find $COPY -type f)
+    updateVersion $COPY_FILES_TO
     updateLicenceDates $(find $COPY -type f)
 
     echo "Update implementation and VTS generic code"
@@ -156,18 +171,12 @@
 echo "Now creating the framework adapter version"
 runIfNeeded $FWK_DIRECTORY createFrameworkAdapter
 
-createVTSXML() {
-    cp -Tar V$BASE_VERSION_UNDERSCORE V$NEW_VERSION_UNDERSCORE
-    cp -Tar effect/{V$BASE_VERSION_UNDERSCORE,V$NEW_VERSION_UNDERSCORE}
-    local -r FILES=$(find {.,effect}/V$NEW_VERSION_UNDERSCORE -type f)
-    updateVersion $FILES
-    updateLicenceDates $FILES
-}
-echo "Now update VTS XML"
-runIfNeeded $VTS_DIRECTORY createVTSXML
-
 echo "Now register new VTS"
+PREV_MODIFIED="$MODIFIED"
 runIfNeeded $(dirname $VTS_LIST) updateAudioVersion -v original_before=1 $(basename $VTS_LIST)
+if [[ "$PREV_MODIFIED" != "$MODIFIED" ]]; then
+    updateAudioVtsTargetVersion -v original_after=1 $(basename $VTS_LIST)
+fi
 
 echo "Now update watchdog"
 runIfNeeded $(dirname $WATCHDOG) updateAudioVersion -v original_before=1 $(basename $WATCHDOG)
diff --git a/audio/common/all-versions/default/7.0/HidlUtils.cpp b/audio/common/all-versions/default/7.0/HidlUtils.cpp
new file mode 100644
index 0000000..5a5b5d2
--- /dev/null
+++ b/audio/common/all-versions/default/7.0/HidlUtils.cpp
@@ -0,0 +1,1158 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+#include <algorithm>
+
+#define LOG_TAG "HidlUtils"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <common/all-versions/HidlSupport.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+status_t HidlUtils::audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_index_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown index channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_in_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown input channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask) {
+    *channelMask = audio_channel_out_mask_to_string(halChannelMask);
+    if (!channelMask->empty() && !xsd::isUnknownAudioChannelMask(*channelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown output channel mask value 0x%X", halChannelMask);
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask) {
+    if (halChannelMask != AUDIO_CHANNEL_NONE) {
+        if (audio_channel_mask_is_valid(halChannelMask)) {
+            switch (audio_channel_mask_get_representation(halChannelMask)) {
+                case AUDIO_CHANNEL_REPRESENTATION_POSITION:
+                    return isInput ? audioInputChannelMaskFromHal(halChannelMask, channelMask)
+                                   : audioOutputChannelMaskFromHal(halChannelMask, channelMask);
+                case AUDIO_CHANNEL_REPRESENTATION_INDEX:
+                    // Index masks do not have direction.
+                    return audioIndexChannelMaskFromHal(halChannelMask, channelMask);
+                    // no default
+            }
+        }
+        *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+        return BAD_VALUE;
+    }
+    *channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return NO_ERROR;
+}
+
+status_t HidlUtils::audioChannelMasksFromHal(const std::vector<std::string>& halChannelMasks,
+                                             hidl_vec<AudioChannelMask>* channelMasks) {
+    hidl_vec<AudioChannelMask> tempChannelMasks;
+    tempChannelMasks.resize(halChannelMasks.size());
+    size_t tempPos = 0;
+    for (const auto& halChannelMask : halChannelMasks) {
+        if (!halChannelMask.empty() && !xsd::isUnknownAudioChannelMask(halChannelMask)) {
+            tempChannelMasks[tempPos++] = halChannelMask;
+        }
+    }
+    if (tempPos == tempChannelMasks.size()) {
+        *channelMasks = std::move(tempChannelMasks);
+    } else {
+        *channelMasks = hidl_vec<AudioChannelMask>(tempChannelMasks.begin(),
+                                                   tempChannelMasks.begin() + tempPos);
+    }
+    return halChannelMasks.size() == channelMasks->size() ? NO_ERROR : BAD_VALUE;
+}
+
+status_t HidlUtils::audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask) {
+    if (!xsd::isUnknownAudioChannelMask(channelMask) &&
+        audio_channel_mask_from_string(channelMask.c_str(), halChannelMask)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown channel mask \"%s\"", channelMask.c_str());
+    *halChannelMask = AUDIO_CHANNEL_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase) {
+    status_t result = NO_ERROR;
+    configBase->sampleRateHz = halConfigBase.sample_rate;
+    CONVERT_CHECKED(
+            audioChannelMaskFromHal(halConfigBase.channel_mask, isInput, &configBase->channelMask),
+            result);
+    CONVERT_CHECKED(audioFormatFromHal(halConfigBase.format, &configBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase) {
+    status_t result = NO_ERROR;
+    halConfigBase->sample_rate = configBase.sampleRateHz;
+    CONVERT_CHECKED(audioChannelMaskToHal(configBase.channelMask, &halConfigBase->channel_mask),
+                    result);
+    CONVERT_CHECKED(audioFormatToHal(configBase.format, &halConfigBase->format), result);
+    return result;
+}
+
+status_t HidlUtils::audioConfigBaseOptionalFromHal(const audio_config_base_t& halConfigBase,
+                                                   bool isInput, bool formatSpecified,
+                                                   bool sampleRateSpecified,
+                                                   bool channelMaskSpecified,
+                                                   AudioConfigBaseOptional* configBase) {
+    status_t result = NO_ERROR;
+    if (formatSpecified) {
+        AudioFormat value;
+        CONVERT_CHECKED(audioFormatFromHal(halConfigBase.format, &value), result);
+        configBase->format.value(std::move(value));
+    } else {
+        configBase->format.unspecified({});
+    }
+    if (sampleRateSpecified) {
+        configBase->sampleRateHz.value(halConfigBase.sample_rate);
+    } else {
+        configBase->sampleRateHz.unspecified({});
+    }
+    if (channelMaskSpecified) {
+        AudioChannelMask value;
+        CONVERT_CHECKED(audioChannelMaskFromHal(halConfigBase.channel_mask, isInput, &value),
+                        result);
+        configBase->channelMask.value(std::move(value));
+    }
+    return result;
+}
+
+status_t HidlUtils::audioConfigBaseOptionalToHal(const AudioConfigBaseOptional& configBase,
+                                                 audio_config_base_t* halConfigBase,
+                                                 bool* formatSpecified, bool* sampleRateSpecified,
+                                                 bool* channelMaskSpecified) {
+    status_t result = NO_ERROR;
+    *formatSpecified = configBase.format.getDiscriminator() ==
+                       AudioConfigBaseOptional::Format::hidl_discriminator::value;
+    if (*formatSpecified) {
+        CONVERT_CHECKED(audioFormatToHal(configBase.format.value(), &halConfigBase->format),
+                        result);
+    }
+    *sampleRateSpecified = configBase.sampleRateHz.getDiscriminator() ==
+                           AudioConfigBaseOptional::SampleRate::hidl_discriminator::value;
+    if (*sampleRateSpecified) {
+        halConfigBase->sample_rate = configBase.sampleRateHz.value();
+    }
+    *channelMaskSpecified = configBase.channelMask.getDiscriminator() ==
+                            AudioConfigBaseOptional::ChannelMask::hidl_discriminator::value;
+    if (*channelMaskSpecified) {
+        CONVERT_CHECKED(
+                audioChannelMaskToHal(configBase.channelMask.value(), &halConfigBase->channel_mask),
+                result);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioContentTypeFromHal(const audio_content_type_t halContentType,
+                                            AudioContentType* contentType) {
+    *contentType = audio_content_type_to_string(halContentType);
+    if (!contentType->empty() && !xsd::isUnknownAudioContentType(*contentType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio content type value 0x%X", halContentType);
+    *contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioContentTypeToHal(const AudioContentType& contentType,
+                                          audio_content_type_t* halContentType) {
+    if (!xsd::isUnknownAudioContentType(contentType) &&
+        audio_content_type_from_string(contentType.c_str(), halContentType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio content type \"%s\"", contentType.c_str());
+    *halContentType = AUDIO_CONTENT_TYPE_UNKNOWN;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device) {
+    *device = audio_device_to_string(halDevice);
+    if (!device->empty() && !xsd::isUnknownAudioDevice(*device)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device value 0x%X", halDevice);
+    *device = toString(xsd::AudioDevice::AUDIO_DEVICE_NONE);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice) {
+    if (!xsd::isUnknownAudioDevice(device) && audio_device_from_string(device.c_str(), halDevice)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio device \"%s\"", device.c_str());
+    *halDevice = AUDIO_DEVICE_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatFromHal(audio_format_t halFormat, AudioFormat* format) {
+    *format = audio_format_to_string(halFormat);
+    if (!format->empty() && !xsd::isUnknownAudioFormat(*format)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format value 0x%X", halFormat);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatsFromHal(const std::vector<std::string>& halFormats,
+                                        hidl_vec<AudioFormat>* formats) {
+    hidl_vec<AudioFormat> tempFormats;
+    tempFormats.resize(halFormats.size());
+    size_t tempPos = 0;
+    for (const auto& halFormat : halFormats) {
+        if (!halFormat.empty() && !xsd::isUnknownAudioFormat(halFormat)) {
+            tempFormats[tempPos++] = halFormat;
+        }
+    }
+    if (tempPos == tempFormats.size()) {
+        *formats = std::move(tempFormats);
+    } else {
+        *formats = hidl_vec<AudioFormat>(tempFormats.begin(), tempFormats.begin() + tempPos);
+    }
+    return halFormats.size() == formats->size() ? NO_ERROR : BAD_VALUE;
+}
+
+status_t HidlUtils::audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat) {
+    if (!xsd::isUnknownAudioFormat(format) && audio_format_from_string(format.c_str(), halFormat)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio format \"%s\"", format.c_str());
+    *halFormat = AUDIO_FORMAT_DEFAULT;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask) {
+    status_t status = NO_ERROR;
+    std::vector<AudioGainMode> result;
+    for (uint32_t bit = 0; halGainModeMask != 0 && bit < sizeof(audio_gain_mode_t) * 8; ++bit) {
+        audio_gain_mode_t flag = static_cast<audio_gain_mode_t>(1u << bit);
+        if ((flag & halGainModeMask) == flag) {
+            AudioGainMode flagStr = audio_gain_mode_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioGainMode(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio gain mode value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+            halGainModeMask = static_cast<audio_gain_mode_t>(halGainModeMask & ~flag);
+        }
+    }
+    *gainModeMask = result;
+    return status;
+}
+
+status_t HidlUtils::audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask) {
+    status_t status = NO_ERROR;
+    *halGainModeMask = {};
+    for (const auto& gainMode : gainModeMask) {
+        audio_gain_mode_t halGainMode;
+        if (!xsd::isUnknownAudioGainMode(gainMode) &&
+            audio_gain_mode_from_string(gainMode.c_str(), &halGainMode)) {
+            *halGainModeMask = static_cast<audio_gain_mode_t>(*halGainModeMask | halGainMode);
+        } else {
+            ALOGE("Unknown audio gain mode \"%s\"", gainMode.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+    *source = audio_source_to_string(halSource);
+    if (!source->empty() && !xsd::isUnknownAudioSource(*source)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source value 0x%X", halSource);
+    *source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+    if (!xsd::isUnknownAudioSource(source) && audio_source_from_string(source.c_str(), halSource)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio source \"%s\"", source.c_str());
+    *halSource = AUDIO_SOURCE_DEFAULT;
+    return BAD_VALUE;
+}
+
+// The "default" value of audio_stream_type_t is represented by an empty string.
+status_t HidlUtils::audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType) {
+    if (halStreamType != AUDIO_STREAM_DEFAULT) {
+        *streamType = audio_stream_type_to_string(halStreamType);
+        if (!streamType->empty() && !xsd::isUnknownAudioStreamType(*streamType)) {
+            return NO_ERROR;
+        }
+        ALOGE("Unknown audio stream type value 0x%X", halStreamType);
+        return BAD_VALUE;
+    } else {
+        *streamType = "";
+        return NO_ERROR;
+    }
+}
+
+status_t HidlUtils::audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType) {
+    if (!streamType.empty()) {
+        if (!xsd::isUnknownAudioStreamType(streamType) &&
+            audio_stream_type_from_string(streamType.c_str(), halStreamType)) {
+            return NO_ERROR;
+        }
+        ALOGE("Unknown audio stream type \"%s\"", streamType.c_str());
+        return BAD_VALUE;
+    } else {
+        *halStreamType = AUDIO_STREAM_DEFAULT;
+        return NO_ERROR;
+    }
+}
+
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config) {
+    status_t result = NO_ERROR;
+    audio_config_base_t halConfigBase = {halConfig.sample_rate, halConfig.channel_mask,
+                                         halConfig.format};
+    CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, isInput, &config->base), result);
+    if (halConfig.offload_info.sample_rate != 0) {
+        config->offloadInfo.info({});
+        CONVERT_CHECKED(
+                audioOffloadInfoFromHal(halConfig.offload_info, &config->offloadInfo.info()),
+                result);
+    }
+    config->frameCount = halConfig.frame_count;
+    return result;
+}
+
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    *halConfig = AUDIO_CONFIG_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(config.base, &halConfigBase), result);
+    halConfig->sample_rate = halConfigBase.sample_rate;
+    halConfig->channel_mask = halConfigBase.channel_mask;
+    halConfig->format = halConfigBase.format;
+    if (config.offloadInfo.getDiscriminator() ==
+        AudioConfig::OffloadInfo::hidl_discriminator::info) {
+        CONVERT_CHECKED(audioOffloadInfoToHal(config.offloadInfo.info(), &halConfig->offload_info),
+                        result);
+    }
+    halConfig->frame_count = config.frameCount;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config) {
+    status_t result = NO_ERROR;
+    config->index = halConfig.index;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halConfig.mode, &config->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halConfig.channel_mask, isInput, &config->channelMask),
+                    result);
+    if (halConfig.mode & AUDIO_GAIN_MODE_JOINT) {
+        config->values.resize(1);
+        config->values[0] = halConfig.values[0];
+    }
+    if (halConfig.mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        config->values.resize(__builtin_popcount(halConfig.channel_mask));
+        for (size_t i = 0; i < config->values.size(); ++i) {
+            config->values[i] = halConfig.values[i];
+        }
+    }
+    config->rampDurationMs = halConfig.ramp_duration_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
+    status_t result = NO_ERROR;
+    halConfig->index = config.index;
+    CONVERT_CHECKED(audioGainModeMaskToHal(config.mode, &halConfig->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(config.channelMask, &halConfig->channel_mask), result);
+    memset(halConfig->values, 0, sizeof(halConfig->values));
+    if (halConfig->mode & AUDIO_GAIN_MODE_JOINT) {
+        if (config.values.size() > 0) {
+            halConfig->values[0] = config.values[0];
+        } else {
+            ALOGE("Empty values vector in AudioGainConfig");
+            result = BAD_VALUE;
+        }
+    }
+    if (halConfig->mode & (AUDIO_GAIN_MODE_CHANNELS | AUDIO_GAIN_MODE_RAMP)) {
+        size_t channelCount = __builtin_popcount(halConfig->channel_mask);
+        size_t valuesCount = config.values.size();
+        if (channelCount != valuesCount) {
+            ALOGE("Wrong number of values in AudioGainConfig, expected: %zu, found: %zu",
+                  channelCount, valuesCount);
+            result = BAD_VALUE;
+            if (channelCount < valuesCount) {
+                valuesCount = channelCount;
+            }
+        }
+        for (size_t i = 0; i < valuesCount; ++i) {
+            halConfig->values[i] = config.values[i];
+        }
+    }
+    halConfig->ramp_duration_ms = config.rampDurationMs;
+    return result;
+}
+
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskFromHal(halGain.mode, &gain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskFromHal(halGain.channel_mask, isInput, &gain->channelMask),
+                    result);
+    gain->minValue = halGain.min_value;
+    gain->maxValue = halGain.max_value;
+    gain->defaultValue = halGain.default_value;
+    gain->stepValue = halGain.step_value;
+    gain->minRampMs = halGain.min_ramp_ms;
+    gain->maxRampMs = halGain.max_ramp_ms;
+    return result;
+}
+
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioGainModeMaskToHal(gain.mode, &halGain->mode), result);
+    CONVERT_CHECKED(audioChannelMaskToHal(gain.channelMask, &halGain->channel_mask), result);
+    halGain->min_value = gain.minValue;
+    halGain->max_value = gain.maxValue;
+    halGain->default_value = gain.defaultValue;
+    halGain->step_value = gain.stepValue;
+    halGain->min_ramp_ms = gain.minRampMs;
+    halGain->max_ramp_ms = gain.maxRampMs;
+    return result;
+}
+
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
+    if (halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED ||
+        halUsage == AUDIO_USAGE_NOTIFICATION_EVENT) {
+        halUsage = AUDIO_USAGE_NOTIFICATION;
+    }
+    *usage = audio_usage_to_string(halUsage);
+    if (!usage->empty() && !xsd::isUnknownAudioUsage(*usage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage %d", halUsage);
+    *usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    if (!xsd::isUnknownAudioUsage(usage) && audio_usage_from_string(usage.c_str(), halUsage)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio usage \"%s\"", usage.c_str());
+    *halUsage = AUDIO_USAGE_UNKNOWN;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
+                                            AudioOffloadInfo* offload) {
+    status_t result = NO_ERROR;
+    audio_config_base_t halConfigBase = {halOffload.sample_rate, halOffload.channel_mask,
+                                         halOffload.format};
+    CONVERT_CHECKED(audioConfigBaseFromHal(halConfigBase, false /*isInput*/, &offload->base),
+                    result);
+    CONVERT_CHECKED(audioStreamTypeFromHal(halOffload.stream_type, &offload->streamType), result);
+    offload->bitRatePerSecond = halOffload.bit_rate;
+    offload->durationMicroseconds = halOffload.duration_us;
+    offload->hasVideo = halOffload.has_video;
+    offload->isStreaming = halOffload.is_streaming;
+    offload->bitWidth = halOffload.bit_width;
+    offload->bufferSize = halOffload.offload_buffer_size;
+    CONVERT_CHECKED(audioUsageFromHal(halOffload.usage, &offload->usage), result);
+    if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
+        offload->encapsulationMode =
+                static_cast<AudioEncapsulationMode>(halOffload.encapsulation_mode);
+        offload->contentId = halOffload.content_id;
+        offload->syncId = halOffload.sync_id;
+    } else {
+        offload->encapsulationMode = AudioEncapsulationMode::NONE;
+        offload->contentId = 0;
+        offload->syncId = 0;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
+    status_t result = NO_ERROR;
+    *halOffload = AUDIO_INFO_INITIALIZER;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    CONVERT_CHECKED(audioConfigBaseToHal(offload.base, &halConfigBase), result);
+    halOffload->sample_rate = halConfigBase.sample_rate;
+    halOffload->channel_mask = halConfigBase.channel_mask;
+    halOffload->format = halConfigBase.format;
+    CONVERT_CHECKED(audioStreamTypeToHal(offload.streamType, &halOffload->stream_type), result);
+    halOffload->bit_rate = offload.bitRatePerSecond;
+    halOffload->duration_us = offload.durationMicroseconds;
+    halOffload->has_video = offload.hasVideo;
+    halOffload->is_streaming = offload.isStreaming;
+    halOffload->bit_width = offload.bitWidth;
+    halOffload->offload_buffer_size = offload.bufferSize;
+    CONVERT_CHECKED(audioUsageToHal(offload.usage, &halOffload->usage), result);
+    halOffload->encapsulation_mode =
+            static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
+    halOffload->content_id = offload.contentId;
+    halOffload->sync_id = offload.syncId;
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    config->id = halConfig.id;
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halConfig.role, halConfig.type,
+                                                 halConfig.ext.device, halConfig.ext.mix,
+                                                 halConfig.ext.session, &config->ext, &isInput),
+                    result);
+    if (audio_port_config_has_input_direction(&halConfig) != isInput) {
+        ALOGE("Inconsistent port config direction data, is input: %d (hal) != %d (converter)",
+              audio_port_config_has_input_direction(&halConfig), isInput);
+        result = BAD_VALUE;
+    }
+    audio_config_base_t halConfigBase = {halConfig.sample_rate, halConfig.channel_mask,
+                                         halConfig.format};
+    CONVERT_CHECKED(
+            audioConfigBaseOptionalFromHal(
+                    halConfigBase, isInput, halConfig.config_mask & AUDIO_PORT_CONFIG_FORMAT,
+                    halConfig.config_mask & AUDIO_PORT_CONFIG_SAMPLE_RATE,
+                    halConfig.config_mask & AUDIO_PORT_CONFIG_CHANNEL_MASK, &config->base),
+            result);
+    if (halConfig.config_mask & AUDIO_PORT_CONFIG_GAIN) {
+        config->gain.config({});
+        CONVERT_CHECKED(audioGainConfigFromHal(halConfig.gain, isInput, &config->gain.config()),
+                        result);
+    } else {
+        config->gain.unspecified({});
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
+    status_t result = NO_ERROR;
+    memset(halConfig, 0, sizeof(audio_port_config));
+    halConfig->id = config.id;
+    halConfig->config_mask = 0;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    bool formatSpecified = false, sRateSpecified = false, channelMaskSpecified = false;
+    CONVERT_CHECKED(audioConfigBaseOptionalToHal(config.base, &halConfigBase, &formatSpecified,
+                                                 &sRateSpecified, &channelMaskSpecified),
+                    result);
+    if (sRateSpecified) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_SAMPLE_RATE;
+        halConfig->sample_rate = halConfigBase.sample_rate;
+    }
+    if (channelMaskSpecified) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_CHANNEL_MASK;
+        halConfig->channel_mask = halConfigBase.channel_mask;
+    }
+    if (formatSpecified) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_FORMAT;
+        halConfig->format = halConfigBase.format;
+    }
+    if (config.gain.getDiscriminator() ==
+        AudioPortConfig::OptionalGain::hidl_discriminator::config) {
+        halConfig->config_mask |= AUDIO_PORT_CONFIG_GAIN;
+        CONVERT_CHECKED(audioGainConfigToHal(config.gain.config(), &halConfig->gain), result);
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(config.ext, &halConfig->role, &halConfig->type,
+                                               &halConfig->ext.device, &halConfig->ext.mix,
+                                               &halConfig->ext.session),
+                    result);
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoFromHal(
+        audio_port_role_t role, audio_port_type_t type,
+        const struct audio_port_config_device_ext& device,
+        const struct audio_port_config_mix_ext& mix,
+        const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+        bool* isInput) {
+    status_t result = NO_ERROR;
+    *isInput = false;
+    switch (type) {
+        case AUDIO_PORT_TYPE_NONE:
+            ext->unspecified({});
+            break;
+        case AUDIO_PORT_TYPE_DEVICE: {
+            *isInput = role == AUDIO_PORT_ROLE_SOURCE;
+            ext->device({});
+            CONVERT_CHECKED(deviceAddressFromHal(device.type, device.address, &ext->device()),
+                            result);
+            break;
+        }
+        case AUDIO_PORT_TYPE_MIX: {
+            *isInput = role == AUDIO_PORT_ROLE_SINK;
+            ext->mix({});
+            ext->mix().ioHandle = mix.handle;
+            if (role == AUDIO_PORT_ROLE_SOURCE) {
+                ext->mix().useCase.stream({});
+                CONVERT_CHECKED(
+                        audioStreamTypeFromHal(mix.usecase.stream, &ext->mix().useCase.stream()),
+                        result);
+            } else if (role == AUDIO_PORT_ROLE_SINK) {
+                ext->mix().useCase.source({});
+                CONVERT_CHECKED(
+                        audioSourceFromHal(mix.usecase.source, &ext->mix().useCase.source()),
+                        result);
+            }
+            break;
+        }
+        case AUDIO_PORT_TYPE_SESSION: {
+            ext->session(session.session);
+            break;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session) {
+    status_t result = NO_ERROR;
+    switch (ext.getDiscriminator()) {
+        case AudioPortExtendedInfo::hidl_discriminator::unspecified:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_NONE;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::device:
+            *role = xsd::isOutputDevice(ext.device().deviceType) ? AUDIO_PORT_ROLE_SINK
+                                                                 : AUDIO_PORT_ROLE_SOURCE;
+            *type = AUDIO_PORT_TYPE_DEVICE;
+            CONVERT_CHECKED(deviceAddressToHal(ext.device(), &device->type, device->address),
+                            result);
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::mix:
+            *type = AUDIO_PORT_TYPE_MIX;
+            switch (ext.mix().useCase.getDiscriminator()) {
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::stream:
+                    *role = AUDIO_PORT_ROLE_SOURCE;
+                    CONVERT_CHECKED(
+                            audioStreamTypeToHal(ext.mix().useCase.stream(), &mix->usecase.stream),
+                            result);
+                    break;
+                case AudioPortExtendedInfo::AudioPortMixExt::UseCase::hidl_discriminator::source:
+                    *role = AUDIO_PORT_ROLE_SINK;
+                    CONVERT_CHECKED(
+                            audioSourceToHal(ext.mix().useCase.source(), &mix->usecase.source),
+                            result);
+                    break;
+            }
+            mix->handle = ext.mix().ioHandle;
+            break;
+        case AudioPortExtendedInfo::hidl_discriminator::session:
+            *role = AUDIO_PORT_ROLE_NONE;
+            *type = AUDIO_PORT_TYPE_SESSION;
+            session->session = static_cast<audio_session_t>(ext.session());
+            break;
+    }
+    return result;
+}
+
+status_t HidlUtils::encapsulationTypeFromHal(audio_encapsulation_type_t halEncapsulationType,
+                                             AudioEncapsulationType* encapsulationType) {
+    *encapsulationType = audio_encapsulation_type_to_string(halEncapsulationType);
+    if (!encapsulationType->empty() && !xsd::isUnknownAudioEncapsulationType(*encapsulationType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio encapsulation type value 0x%X", halEncapsulationType);
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::encapsulationTypeToHal(const AudioEncapsulationType& encapsulationType,
+                                           audio_encapsulation_type_t* halEncapsulationType) {
+    if (!xsd::isUnknownAudioEncapsulationType(encapsulationType) &&
+        audio_encapsulation_type_from_string(encapsulationType.c_str(), halEncapsulationType)) {
+        return NO_ERROR;
+    }
+    ALOGE("Unknown audio encapsulation type \"%s\"", encapsulationType.c_str());
+    *halEncapsulationType = AUDIO_ENCAPSULATION_TYPE_NONE;
+    return BAD_VALUE;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+    struct audio_port_v7 halPortV7 = {};
+    audio_populate_audio_port_v7(&halPort, &halPortV7);
+    return audioPortFromHal(halPortV7, port);
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+    status_t result = NO_ERROR;
+    struct audio_port_v7 halPortV7 = {};
+    CONVERT_CHECKED(audioPortToHal(port, &halPortV7), result);
+    if (!audio_populate_audio_port(&halPortV7, halPort)) {
+        result = BAD_VALUE;
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port) {
+    status_t result = NO_ERROR;
+    bool isInput = false;
+    port->id = halPort.id;
+    port->name.setToExternal(halPort.name, strlen(halPort.name));
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    switch (halPort.type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halDevice.type = halPort.ext.device.type;
+            memcpy(halDevice.address, halPort.ext.device.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halMix.handle = halPort.ext.mix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halSession.session = halPort.ext.session.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortExtendedInfoFromHal(halPort.role, halPort.type, halDevice, halMix,
+                                                 halSession, &port->ext, &isInput),
+                    result);
+    CONVERT_CHECKED(audioTransportsFromHal(halPort, isInput, &port->transports), result);
+    port->gains.resize(halPort.num_gains);
+    for (size_t i = 0; i < halPort.num_gains; ++i) {
+        CONVERT_CHECKED(audioGainFromHal(halPort.gains[i], isInput, &port->gains[i]), result);
+    }
+    CONVERT_CHECKED(audioPortConfigFromHal(halPort.active_config, &port->activeConfig), result);
+    return result;
+}
+
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort) {
+    status_t result = NO_ERROR;
+    halPort->id = port.id;
+    strncpy(halPort->name, port.name.c_str(), AUDIO_PORT_MAX_NAME_LEN);
+    halPort->name[AUDIO_PORT_MAX_NAME_LEN - 1] = '\0';
+    if (port.name.size() >= AUDIO_PORT_MAX_NAME_LEN) {
+        ALOGE("HIDL Audio Port name is too long: %zu", port.name.size());
+        result = BAD_VALUE;
+    }
+    CONVERT_CHECKED(audioTransportsToHal(port.transports, halPort), result);
+    halPort->num_gains = port.gains.size();
+    if (halPort->num_gains > AUDIO_PORT_MAX_GAINS) {
+        ALOGE("HIDL Audio Port has too many gains: %u", halPort->num_gains);
+        halPort->num_gains = AUDIO_PORT_MAX_GAINS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halPort->num_gains; ++i) {
+        CONVERT_CHECKED(audioGainToHal(port.gains[i], &halPort->gains[i]), result);
+    }
+    // HAL uses slightly different but convertible structures for the extended info in port
+    // and port config structures.
+    struct audio_port_config_device_ext halDevice = {};
+    struct audio_port_config_mix_ext halMix = {};
+    struct audio_port_config_session_ext halSession = {};
+    CONVERT_CHECKED(audioPortExtendedInfoToHal(port.ext, &halPort->role, &halPort->type, &halDevice,
+                                               &halMix, &halSession),
+                    result);
+    switch (halPort->type) {
+        case AUDIO_PORT_TYPE_NONE:
+            break;
+        case AUDIO_PORT_TYPE_DEVICE:
+            halPort->ext.device.type = halDevice.type;
+            memcpy(halPort->ext.device.address, halDevice.address, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+            break;
+        case AUDIO_PORT_TYPE_MIX:
+            halPort->ext.mix.handle = halMix.handle;
+            break;
+        case AUDIO_PORT_TYPE_SESSION:
+            halPort->ext.session.session = halSession.session;
+            break;
+    }
+    CONVERT_CHECKED(audioPortConfigToHal(port.activeConfig, &halPort->active_config), result);
+    return result;
+}
+
+status_t HidlUtils::audioTransportsFromHal(const struct audio_port_v7& halPort, bool isInput,
+                                           hidl_vec<AudioTransport>* transports) {
+    if (halPort.num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES ||
+        halPort.num_extra_audio_descriptors > AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+        ALOGE("%s, too many audio profiles(%u) or extra audio descriptors(%u)", __func__,
+              halPort.num_audio_profiles, halPort.num_extra_audio_descriptors);
+        return BAD_VALUE;
+    }
+    status_t result = NO_ERROR;
+    transports->resize(halPort.num_audio_profiles + halPort.num_extra_audio_descriptors);
+    size_t idx = 0;
+    for (size_t i = 0; i < halPort.num_audio_profiles; ++i) {
+        auto& transport = (*transports)[idx++];
+        transport.audioCapability.profile({});
+        CONVERT_CHECKED(audioProfileFromHal(halPort.audio_profiles[i], isInput,
+                                            &transport.audioCapability.profile()),
+                        result);
+        CONVERT_CHECKED(encapsulationTypeFromHal(halPort.audio_profiles[i].encapsulation_type,
+                                                 &transport.encapsulationType),
+                        result);
+    }
+    for (size_t i = 0; i < halPort.num_extra_audio_descriptors; ++i) {
+        switch (halPort.extra_audio_descriptors[i].standard) {
+            case AUDIO_STANDARD_EDID: {
+                const struct audio_extra_audio_descriptor* extraAudioDescriptor =
+                        &halPort.extra_audio_descriptors[i];
+                if (extraAudioDescriptor->descriptor_length <= EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+                    auto& transport = (*transports)[idx++];
+                    transport.audioCapability.edid(
+                            hidl_vec<uint8_t>(extraAudioDescriptor->descriptor,
+                                              extraAudioDescriptor->descriptor +
+                                                      extraAudioDescriptor->descriptor_length));
+                    CONVERT_CHECKED(
+                            encapsulationTypeFromHal(extraAudioDescriptor->encapsulation_type,
+                                                     &transport.encapsulationType),
+                            result);
+                } else {
+                    ALOGE("%s, invalid descriptor length %u", __func__,
+                          extraAudioDescriptor->descriptor_length);
+                    result = BAD_VALUE;
+                }
+            } break;
+            case AUDIO_STANDARD_NONE:
+            default:
+                ALOGE("%s, invalid standard %u", __func__,
+                      halPort.extra_audio_descriptors[i].standard);
+                result = BAD_VALUE;
+                break;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioTransportsToHal(const hidl_vec<AudioTransport>& transports,
+                                         struct audio_port_v7* halPort) {
+    status_t result = NO_ERROR;
+    halPort->num_audio_profiles = 0;
+    halPort->num_extra_audio_descriptors = 0;
+    for (const auto& transport : transports) {
+        switch (transport.audioCapability.getDiscriminator()) {
+            case AudioTransport::AudioCapability::hidl_discriminator::profile:
+                if (halPort->num_audio_profiles > AUDIO_PORT_MAX_AUDIO_PROFILES) {
+                    ALOGE("%s, too many audio profiles", __func__);
+                    result = BAD_VALUE;
+                    break;
+                }
+                CONVERT_CHECKED(
+                        audioProfileToHal(transport.audioCapability.profile(),
+                                          &halPort->audio_profiles[halPort->num_audio_profiles]),
+                        result);
+                CONVERT_CHECKED(encapsulationTypeToHal(
+                                        transport.encapsulationType,
+                                        &halPort->audio_profiles[halPort->num_audio_profiles++]
+                                                 .encapsulation_type),
+                                result);
+                break;
+            case AudioTransport::AudioCapability::hidl_discriminator::edid:
+                if (halPort->num_extra_audio_descriptors > AUDIO_PORT_MAX_EXTRA_AUDIO_DESCRIPTORS) {
+                    ALOGE("%s, too many extra audio descriptors", __func__);
+                    result = BAD_VALUE;
+                    break;
+                }
+                if (transport.audioCapability.edid().size() > EXTRA_AUDIO_DESCRIPTOR_SIZE) {
+                    ALOGE("%s, wrong edid size %zu", __func__,
+                          transport.audioCapability.edid().size());
+                    result = BAD_VALUE;
+                    break;
+                }
+                struct audio_extra_audio_descriptor* extraAudioDescriptor =
+                        &halPort->extra_audio_descriptors[halPort->num_extra_audio_descriptors++];
+                extraAudioDescriptor->standard = AUDIO_STANDARD_EDID;
+                extraAudioDescriptor->descriptor_length = transport.audioCapability.edid().size();
+                memcpy(extraAudioDescriptor->descriptor, transport.audioCapability.edid().data(),
+                       transport.audioCapability.edid().size() * sizeof(uint8_t));
+
+                CONVERT_CHECKED(encapsulationTypeToHal(transport.encapsulationType,
+                                                       &extraAudioDescriptor->encapsulation_type),
+                                result);
+                break;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatFromHal(halProfile.format, &profile->format), result);
+    profile->sampleRates.resize(halProfile.num_sample_rates);
+    for (size_t i = 0; i < halProfile.num_sample_rates; ++i) {
+        profile->sampleRates[i] = halProfile.sample_rates[i];
+    }
+    profile->channelMasks.resize(halProfile.num_channel_masks);
+    for (size_t i = 0; i < halProfile.num_channel_masks; ++i) {
+        CONVERT_CHECKED(audioChannelMaskFromHal(halProfile.channel_masks[i], isInput,
+                                                &profile->channelMasks[i]),
+                        result);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioFormatToHal(profile.format, &halProfile->format), result);
+    memset(halProfile->sample_rates, 0, sizeof(halProfile->sample_rates));
+    halProfile->num_sample_rates = profile.sampleRates.size();
+    if (halProfile->num_sample_rates > AUDIO_PORT_MAX_SAMPLING_RATES) {
+        ALOGE("HIDL Audio profile has too many sample rates: %u", halProfile->num_sample_rates);
+        halProfile->num_sample_rates = AUDIO_PORT_MAX_SAMPLING_RATES;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_sample_rates; ++i) {
+        halProfile->sample_rates[i] = profile.sampleRates[i];
+    }
+    memset(halProfile->channel_masks, 0, sizeof(halProfile->channel_masks));
+    halProfile->num_channel_masks = profile.channelMasks.size();
+    if (halProfile->num_channel_masks > AUDIO_PORT_MAX_CHANNEL_MASKS) {
+        ALOGE("HIDL Audio profile has too many channel masks: %u", halProfile->num_channel_masks);
+        halProfile->num_channel_masks = AUDIO_PORT_MAX_CHANNEL_MASKS;
+        result = BAD_VALUE;
+    }
+    for (size_t i = 0; i < halProfile->num_channel_masks; ++i) {
+        CONVERT_CHECKED(
+                audioChannelMaskToHal(profile.channelMasks[i], &halProfile->channel_masks[i]),
+                status);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioTagsFromHal(const std::vector<std::string>& strTags,
+                                     hidl_vec<AudioTag>* tags) {
+    status_t result = NO_ERROR;
+    tags->resize(strTags.size());
+    size_t to = 0;
+    for (size_t from = 0; from < strTags.size(); ++from) {
+        const auto& tag = strTags[from];
+        if (xsd::isVendorExtension(tag)) {
+            (*tags)[to++] = tag;
+        } else {
+            ALOGE("Vendor extension tag is ill-formed: \"%s\"", tag.c_str());
+            result = BAD_VALUE;
+        }
+    }
+    if (to != strTags.size()) {
+        tags->resize(to);
+    }
+    return result;
+}
+
+status_t HidlUtils::audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags) {
+    memset(halTags, 0, AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
+    status_t result = NO_ERROR;
+    std::ostringstream halTagsBuffer;
+    bool hasValue = false;
+    for (const auto& tag : tags) {
+        if (hasValue) {
+            halTagsBuffer << sAudioTagSeparator;
+        }
+        if (xsd::isVendorExtension(tag) && strchr(tag.c_str(), sAudioTagSeparator) == nullptr) {
+            halTagsBuffer << tag;
+            hasValue = true;
+        } else {
+            ALOGE("Vendor extension tag is ill-formed: \"%s\"", tag.c_str());
+            result = BAD_VALUE;
+        }
+    }
+    std::string fullHalTags{std::move(halTagsBuffer.str())};
+    strncpy(halTags, fullHalTags.c_str(), AUDIO_ATTRIBUTES_TAGS_MAX_SIZE);
+    CONVERT_CHECKED(fullHalTags.length() <= AUDIO_ATTRIBUTES_TAGS_MAX_SIZE ? NO_ERROR : BAD_VALUE,
+                    result);
+    return result;
+}
+
+hidl_vec<AudioTag> HidlUtils::filterOutNonVendorTags(const hidl_vec<AudioTag>& tags) {
+    hidl_vec<AudioTag> result;
+    result.resize(tags.size());
+    size_t resultIdx = 0;
+    for (const auto& tag : tags) {
+        if (xsd::maybeVendorExtension(tag)) {
+            result[resultIdx++] = tag;
+        }
+    }
+    if (resultIdx != result.size()) {
+        result.resize(resultIdx);
+    }
+    return result;
+}
+
+std::vector<std::string> HidlUtils::filterOutNonVendorTags(const std::vector<std::string>& tags) {
+    std::vector<std::string> result;
+    std::copy_if(tags.begin(), tags.end(), std::back_inserter(result), xsd::maybeVendorExtension);
+    return result;
+}
+
+std::vector<std::string> HidlUtils::splitAudioTags(const char* halTags) {
+    return utils::splitString(halTags, sAudioTagSeparator);
+}
+
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeFromHal(halDeviceType, &device->deviceType), result);
+    if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+        device->address.mac({});
+        if (halDeviceAddress != nullptr) {
+            auto& mac = device->address.mac();
+            int status = sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &mac[0], &mac[1],
+                                &mac[2], &mac[3], &mac[4], &mac[5]);
+            if (status != 6) {
+                ALOGE("BT A2DP device \"%s\" MAC address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have a MAC address", halDeviceAddress);
+            result = BAD_VALUE;
+        }
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+        device->address.ipv4({});
+        if (halDeviceAddress != nullptr) {
+            auto& ipv4 = device->address.ipv4();
+            int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &ipv4[0], &ipv4[1],
+                                &ipv4[2], &ipv4[3]);
+            if (status != 4) {
+                ALOGE("IP device \"%s\" IPv4 address \"%s\" is invalid", device->deviceType.c_str(),
+                      halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("IP device \"%s\" does not have an IPv4 address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+        device->address.alsa({});
+        if (halDeviceAddress != nullptr) {
+            auto& alsa = device->address.alsa();
+            int status = sscanf(halDeviceAddress, "card=%d;device=%d", &alsa.card, &alsa.device);
+            if (status != 2) {
+                ALOGE("USB device \"%s\" ALSA address \"%s\" is invalid",
+                      device->deviceType.c_str(), halDeviceAddress);
+                result = BAD_VALUE;
+            }
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address", device->deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        device->address.id(halDeviceAddress != nullptr ? halDeviceAddress : "");
+    }
+    return result;
+}
+
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(audioDeviceTypeToHal(device.deviceType, halDeviceType), result);
+    memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+    if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::mac) {
+            const auto& mac = device.address.mac();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN,
+                     "%02X:%02X:%02X:%02X:%02X:%02X", mac[0], mac[1], mac[2], mac[3], mac[4],
+                     mac[5]);
+        } else {
+            ALOGE("BT A2DP device \"%s\" does not have MAC address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::ipv4) {
+            const auto& ipv4 = device.address.ipv4();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d", ipv4[0],
+                     ipv4[1], ipv4[2], ipv4[3]);
+        } else {
+            ALOGE("IP device \"%s\" does not have IPv4 address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::alsa) {
+            const auto& alsa = device.address.alsa();
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d", alsa.card,
+                     alsa.device);
+        } else {
+            ALOGE("USB device \"%s\" does not have ALSA address set", device.deviceType.c_str());
+            result = BAD_VALUE;
+        }
+    } else {
+        // Any other device type uses the 'id' field.
+        if (device.address.getDiscriminator() == DeviceAddress::Address::hidl_discriminator::id) {
+            snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+                     device.address.id().c_str());
+        }
+    }
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/Android.bp b/audio/common/all-versions/default/Android.bp
index 0eb4a71..8f55744 100644
--- a/audio/common/all-versions/default/Android.bp
+++ b/audio/common/all-versions/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.audio.common-util",
     defaults: ["hidl_defaults"],
@@ -36,7 +45,16 @@
     ],
     export_header_lib_headers: [
         "android.hardware.audio.common.util@all-versions",
-    ]
+    ],
+}
+
+filegroup {
+    name: "android.hardware.audio.common-util@2-6",
+    srcs: [
+        "HidlUtils.cpp",
+        "HidlUtilsCommon.cpp",
+        "UuidUtils.cpp",
+    ],
 }
 
 cc_defaults {
@@ -44,9 +62,6 @@
     defaults: ["hidl_defaults"],
 
     vendor_available: true,
-    srcs: [
-        "HidlUtils.cpp",
-    ],
 
     export_include_dirs: ["."],
 
@@ -57,7 +72,7 @@
         "android.hardware.audio.common-util",
     ],
     export_shared_lib_headers: [
-        "android.hardware.audio.common-util"
+        "android.hardware.audio.common-util",
     ],
 
     header_libs: [
@@ -69,6 +84,7 @@
 cc_library_shared {
     name: "android.hardware.audio.common@2.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@2.0",
     ],
@@ -76,12 +92,13 @@
         "-DMAJOR_VERSION=2",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_library_shared {
     name: "android.hardware.audio.common@4.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@4.0",
     ],
@@ -89,12 +106,13 @@
         "-DMAJOR_VERSION=4",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_library_shared {
     name: "android.hardware.audio.common@5.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@5.0",
     ],
@@ -102,12 +120,13 @@
         "-DMAJOR_VERSION=5",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
-cc_library_shared {
+cc_library {
     name: "android.hardware.audio.common@6.0-util",
     defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [":android.hardware.audio.common-util@2-6"],
     shared_libs: [
         "android.hardware.audio.common@6.0",
     ],
@@ -115,5 +134,83 @@
         "-DMAJOR_VERSION=6",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
+}
+
+cc_library {
+    name: "android.hardware.audio.common@7.0-util",
+    defaults: ["android.hardware.audio.common-util_default"],
+    srcs: [
+        "7.0/HidlUtils.cpp",
+        "HidlUtilsCommon.cpp",
+        "UuidUtils.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "libbase",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio.common@6.0-util_tests",
+    defaults: ["android.hardware.audio.common-util_default"],
+
+    srcs: ["tests/hidlutils6_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V6.
+    static_libs: [
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
+}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio.common@7.0-util_tests",
+    defaults: ["android.hardware.audio.common-util_default"],
+
+    srcs: ["tests/hidlutils_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V7.
+    static_libs: [
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.common@7.0",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libxml2",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
 }
diff --git a/audio/common/all-versions/default/HidlUtils.cpp b/audio/common/all-versions/default/HidlUtils.cpp
index a470c9c..c0dcd80 100644
--- a/audio/common/all-versions/default/HidlUtils.cpp
+++ b/audio/common/all-versions/default/HidlUtils.cpp
@@ -28,7 +28,7 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config) {
+status_t HidlUtils::audioConfigFromHal(const audio_config_t& halConfig, bool, AudioConfig* config) {
     config->sampleRateHz = halConfig.sample_rate;
     config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
     config->format = AudioFormat(halConfig.format);
@@ -37,17 +37,18 @@
     return status;
 }
 
-void HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
+status_t HidlUtils::audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig) {
     memset(halConfig, 0, sizeof(audio_config_t));
     halConfig->sample_rate = config.sampleRateHz;
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
     halConfig->format = static_cast<audio_format_t>(config.format);
     audioOffloadInfoToHal(config.offloadInfo, &halConfig->offload_info);
     halConfig->frame_count = config.frameCount;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig,
-                                       AudioGainConfig* config) {
+status_t HidlUtils::audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool,
+                                           AudioGainConfig* config) {
     config->index = halConfig.index;
     config->mode = EnumBitfield<AudioGainMode>(halConfig.mode);
     config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
@@ -55,10 +56,11 @@
         config->values[i] = halConfig.values[i];
     }
     config->rampDurationMs = halConfig.ramp_duration_ms;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig) {
+status_t HidlUtils::audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig) {
     halConfig->index = config.index;
     halConfig->mode = static_cast<audio_gain_mode_t>(config.mode);
     halConfig->channel_mask = static_cast<audio_channel_mask_t>(config.channelMask);
@@ -67,9 +69,10 @@
         halConfig->values[i] = config.values[i];
     }
     halConfig->ramp_duration_ms = config.rampDurationMs;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain) {
+status_t HidlUtils::audioGainFromHal(const struct audio_gain& halGain, bool, AudioGain* gain) {
     gain->mode = EnumBitfield<AudioGainMode>(halGain.mode);
     gain->channelMask = EnumBitfield<AudioChannelMask>(halGain.channel_mask);
     gain->minValue = halGain.min_value;
@@ -78,9 +81,10 @@
     gain->stepValue = halGain.step_value;
     gain->minRampMs = halGain.min_ramp_ms;
     gain->maxRampMs = halGain.max_ramp_ms;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
+status_t HidlUtils::audioGainToHal(const AudioGain& gain, struct audio_gain* halGain) {
     halGain->mode = static_cast<audio_gain_mode_t>(gain.mode);
     halGain->channel_mask = static_cast<audio_channel_mask_t>(gain.channelMask);
     halGain->min_value = gain.minValue;
@@ -89,22 +93,26 @@
     halGain->step_value = gain.stepValue;
     halGain->min_ramp_ms = gain.minRampMs;
     halGain->max_ramp_ms = gain.maxRampMs;
+    return NO_ERROR;
 }
 
-AudioUsage HidlUtils::audioUsageFromHal(const audio_usage_t halUsage) {
+status_t HidlUtils::audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage) {
     switch (halUsage) {
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_REQUEST:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_INSTANT:
         case AUDIO_USAGE_NOTIFICATION_COMMUNICATION_DELAYED:
         case AUDIO_USAGE_NOTIFICATION_EVENT:
-            return AudioUsage::NOTIFICATION;
+            *usage = AudioUsage::NOTIFICATION;
+            break;
         default:
-            return static_cast<AudioUsage>(halUsage);
+            *usage = static_cast<AudioUsage>(halUsage);
     }
+    return NO_ERROR;
 }
 
-audio_usage_t HidlUtils::audioUsageToHal(const AudioUsage usage) {
-    return static_cast<audio_usage_t>(usage);
+status_t HidlUtils::audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage) {
+    *halUsage = static_cast<audio_usage_t>(usage);
+    return NO_ERROR;
 }
 
 status_t HidlUtils::audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
@@ -119,7 +127,7 @@
     offload->isStreaming = halOffload.is_streaming;
     offload->bitWidth = halOffload.bit_width;
     offload->bufferSize = halOffload.offload_buffer_size;
-    offload->usage = audioUsageFromHal(halOffload.usage);
+    audioUsageFromHal(halOffload.usage, &offload->usage);
 #if MAJOR_VERSION >= 6
     if (halOffload.version >= AUDIO_OFFLOAD_INFO_VERSION_0_2) {
         offload->encapsulationMode =
@@ -139,11 +147,11 @@
         return BAD_VALUE;
     }
 #endif
-    return OK;
+    return NO_ERROR;
 }
 
-void HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload) {
+status_t HidlUtils::audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload) {
     *halOffload = AUDIO_INFO_INITIALIZER;
     halOffload->sample_rate = offload.sampleRateHz;
     halOffload->channel_mask = static_cast<audio_channel_mask_t>(offload.channelMask);
@@ -155,7 +163,7 @@
     halOffload->is_streaming = offload.isStreaming;
     halOffload->bit_width = offload.bitWidth;
     halOffload->offload_buffer_size = offload.bufferSize;
-    halOffload->usage = audioUsageToHal(offload.usage);
+    audioUsageToHal(offload.usage, &halOffload->usage);
 #if MAJOR_VERSION >= 6
     halOffload->encapsulation_mode =
             static_cast<audio_encapsulation_mode_t>(offload.encapsulationMode);
@@ -164,10 +172,11 @@
 #else
     // offload doesn't contain encapsulationMode, contentId, syncId, so this is OK.
 #endif
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config) {
+status_t HidlUtils::audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config) {
     config->id = halConfig.id;
     config->role = AudioPortRole(halConfig.role);
     config->type = AudioPortType(halConfig.type);
@@ -175,7 +184,7 @@
     config->sampleRateHz = halConfig.sample_rate;
     config->channelMask = EnumBitfield<AudioChannelMask>(halConfig.channel_mask);
     config->format = AudioFormat(halConfig.format);
-    audioGainConfigFromHal(halConfig.gain, &config->gain);
+    audioGainConfigFromHal(halConfig.gain, false /*isInput--ignored*/, &config->gain);
     switch (halConfig.type) {
         case AUDIO_PORT_TYPE_NONE:
             break;
@@ -201,10 +210,11 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig) {
+status_t HidlUtils::audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig) {
     memset(halConfig, 0, sizeof(audio_port_config));
     halConfig->id = config.id;
     halConfig->role = static_cast<audio_port_role_t>(config.role);
@@ -242,27 +252,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs) {
-    configs->resize(numHalConfigs);
-    for (unsigned int i = 0; i < numHalConfigs; ++i) {
-        audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
-    }
-}
-
-std::unique_ptr<audio_port_config[]> HidlUtils::audioPortConfigsToHal(
-    const hidl_vec<AudioPortConfig>& configs) {
-    std::unique_ptr<audio_port_config[]> halConfigs(new audio_port_config[configs.size()]);
-    for (size_t i = 0; i < configs.size(); ++i) {
-        audioPortConfigToHal(configs[i], &halConfigs[i]);
-    }
-    return halConfigs;
-}
-
-void HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
+status_t HidlUtils::audioPortFromHal(const struct audio_port& halPort, AudioPort* port) {
     port->id = halPort.id;
     port->role = AudioPortRole(halPort.role);
     port->type = AudioPortType(halPort.type);
@@ -281,7 +274,7 @@
     }
     port->gains.resize(halPort.num_gains);
     for (size_t i = 0; i < halPort.num_gains; ++i) {
-        audioGainFromHal(halPort.gains[i], &port->gains[i]);
+        audioGainFromHal(halPort.gains[i], false /*isInput--ignored*/, &port->gains[i]);
     }
     audioPortConfigFromHal(halPort.active_config, &port->activeConfig);
     switch (halPort.type) {
@@ -305,9 +298,10 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
+status_t HidlUtils::audioPortToHal(const AudioPort& port, struct audio_port* halPort) {
     memset(halPort, 0, sizeof(audio_port));
     halPort->id = port.id;
     halPort->role = static_cast<audio_port_role_t>(port.role);
@@ -356,23 +350,20 @@
             break;
         }
     }
+    return NO_ERROR;
 }
 
-void HidlUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
-    uuid->timeLow = halUuid.timeLow;
-    uuid->timeMid = halUuid.timeMid;
-    uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
-    uuid->variantAndClockSeqHigh = halUuid.clockSeq;
-    memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
+#if MAJOR_VERSION >= 5
+status_t HidlUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+    return deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
 }
 
-void HidlUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
-    halUuid->timeLow = uuid.timeLow;
-    halUuid->timeMid = uuid.timeMid;
-    halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
-    halUuid->clockSeq = uuid.variantAndClockSeqHigh;
-    memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+status_t HidlUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+    return deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
 }
+#endif
 
 }  // namespace implementation
 }  // namespace CPP_VERSION
diff --git a/audio/common/all-versions/default/HidlUtils.h b/audio/common/all-versions/default/HidlUtils.h
index ef6dee3..98ecc07 100644
--- a/audio/common/all-versions/default/HidlUtils.h
+++ b/audio/common/all-versions/default/HidlUtils.h
@@ -20,11 +20,11 @@
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
 
 #include <memory>
+#include <string>
+#include <vector>
 
 #include <system/audio.h>
 
-using ::android::hardware::hidl_vec;
-
 namespace android {
 namespace hardware {
 namespace audio {
@@ -32,44 +32,240 @@
 namespace CPP_VERSION {
 namespace implementation {
 
+using ::android::hardware::hidl_vec;
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 
-class HidlUtils {
-  public:
-    // A failure here indicates a platform config that is incompatible with
-    // the compiled HIDL interface version.
-    static status_t audioConfigFromHal(const audio_config_t& halConfig, AudioConfig* config);
-
-    static void audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
-    static void audioGainConfigFromHal(const struct audio_gain_config& halConfig,
-                                       AudioGainConfig* config);
-    static void audioGainConfigToHal(const AudioGainConfig& config,
-                                     struct audio_gain_config* halConfig);
-    static void audioGainFromHal(const struct audio_gain& halGain, AudioGain* gain);
-    static void audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
-    static AudioUsage audioUsageFromHal(const audio_usage_t halUsage);
-    static audio_usage_t audioUsageToHal(const AudioUsage usage);
-    // A failure here indicates a platform offload info that is incompatible with
-    // the compiled HIDL interface version.
+struct HidlUtils {
+    static status_t audioConfigFromHal(const audio_config_t& halConfig, bool isInput,
+                                       AudioConfig* config);
+    static status_t audioConfigToHal(const AudioConfig& config, audio_config_t* halConfig);
+#if MAJOR_VERSION >= 4
+    static status_t audioContentTypeFromHal(const audio_content_type_t halContentType,
+                                            AudioContentType* contentType);
+    static status_t audioContentTypeToHal(const AudioContentType& contentType,
+                                          audio_content_type_t* halContentType);
+#endif
+    static status_t audioGainConfigFromHal(const struct audio_gain_config& halConfig, bool isInput,
+                                           AudioGainConfig* config);
+    static status_t audioGainConfigToHal(const AudioGainConfig& config,
+                                         struct audio_gain_config* halConfig);
+    static status_t audioGainFromHal(const struct audio_gain& halGain, bool isInput,
+                                     AudioGain* gain);
+    static status_t audioGainToHal(const AudioGain& gain, struct audio_gain* halGain);
+    static status_t audioUsageFromHal(audio_usage_t halUsage, AudioUsage* usage);
+    static status_t audioUsageToHal(const AudioUsage& usage, audio_usage_t* halUsage);
     static status_t audioOffloadInfoFromHal(const audio_offload_info_t& halOffload,
                                             AudioOffloadInfo* offload);
-    static void audioOffloadInfoToHal(const AudioOffloadInfo& offload,
-                                      audio_offload_info_t* halOffload);
-    static void audioPortConfigFromHal(const struct audio_port_config& halConfig,
-                                       AudioPortConfig* config);
-    static void audioPortConfigToHal(const AudioPortConfig& config,
-                                     struct audio_port_config* halConfig);
-    static void audioPortConfigsFromHal(unsigned int numHalConfigs,
-                                        const struct audio_port_config* halConfigs,
-                                        hidl_vec<AudioPortConfig>* configs);
-    static std::unique_ptr<audio_port_config[]> audioPortConfigsToHal(
-            const hidl_vec<AudioPortConfig>& configs);
-    static void audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
-    static void audioPortToHal(const AudioPort& port, struct audio_port* halPort);
-    static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
-    static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+    static status_t audioOffloadInfoToHal(const AudioOffloadInfo& offload,
+                                          audio_offload_info_t* halOffload);
+    static status_t audioPortConfigFromHal(const struct audio_port_config& halConfig,
+                                           AudioPortConfig* config);
+    static status_t audioPortConfigToHal(const AudioPortConfig& config,
+                                         struct audio_port_config* halConfig);
+    static status_t audioPortConfigsFromHal(unsigned int numHalConfigs,
+                                            const struct audio_port_config* halConfigs,
+                                            hidl_vec<AudioPortConfig>* configs);
+    static status_t audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+                                          std::unique_ptr<audio_port_config[]>* halConfigs);
+    static status_t audioPortFromHal(const struct audio_port& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port* halPort);
+    static status_t audioSourceFromHal(audio_source_t halSource, AudioSource* source);
+    static status_t audioSourceToHal(const AudioSource& source, audio_source_t* halSource);
+#if MAJOR_VERSION >= 5
+    static status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress);
+    static status_t deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device);
+#endif
+
+#if MAJOR_VERSION >= 7
+    static constexpr char sAudioTagSeparator = ';';
+
+    static status_t audioChannelMaskFromHal(audio_channel_mask_t halChannelMask, bool isInput,
+                                            AudioChannelMask* channelMask);
+    static status_t audioChannelMasksFromHal(const std::vector<std::string>& halChannelMasks,
+                                             hidl_vec<AudioChannelMask>* channelMasks);
+    static status_t audioChannelMaskToHal(const AudioChannelMask& channelMask,
+                                          audio_channel_mask_t* halChannelMask);
+    static status_t audioConfigBaseFromHal(const audio_config_base_t& halConfigBase, bool isInput,
+                                           AudioConfigBase* configBase);
+    static status_t audioConfigBaseToHal(const AudioConfigBase& configBase,
+                                         audio_config_base_t* halConfigBase);
+    static status_t audioConfigBaseOptionalFromHal(const audio_config_base_t& halConfigBase,
+                                                   bool isInput, bool formatSpecified,
+                                                   bool sampleRateSpecified,
+                                                   bool channelMaskSpecified,
+                                                   AudioConfigBaseOptional* configBase);
+    static status_t audioConfigBaseOptionalToHal(const AudioConfigBaseOptional& configBase,
+                                                 audio_config_base_t* halConfigBase,
+                                                 bool* formatSpecified, bool* sampleRateSpecified,
+                                                 bool* channelMaskSpecified);
+    static status_t audioDeviceTypeFromHal(audio_devices_t halDevice, AudioDevice* device);
+    static status_t audioDeviceTypeToHal(const AudioDevice& device, audio_devices_t* halDevice);
+    static status_t audioFormatFromHal(audio_format_t halFormat, AudioFormat* format);
+    static status_t audioFormatsFromHal(const std::vector<std::string>& halFormats,
+                                        hidl_vec<AudioFormat>* formats);
+    static status_t audioFormatToHal(const AudioFormat& format, audio_format_t* halFormat);
+    static status_t audioGainModeMaskFromHal(audio_gain_mode_t halGainModeMask,
+                                             hidl_vec<AudioGainMode>* gainModeMask);
+    static status_t audioGainModeMaskToHal(const hidl_vec<AudioGainMode>& gainModeMask,
+                                           audio_gain_mode_t* halGainModeMask);
+    static status_t audioPortFromHal(const struct audio_port_v7& halPort, AudioPort* port);
+    static status_t audioPortToHal(const AudioPort& port, struct audio_port_v7* halPort);
+    static status_t audioProfileFromHal(const struct audio_profile& halProfile, bool isInput,
+                                        AudioProfile* profile);
+    static status_t audioProfileToHal(const AudioProfile& profile,
+                                      struct audio_profile* halProfile);
+    static status_t audioStreamTypeFromHal(audio_stream_type_t halStreamType,
+                                           AudioStreamType* streamType);
+    static status_t audioStreamTypeToHal(const AudioStreamType& streamType,
+                                         audio_stream_type_t* halStreamType);
+    static status_t audioTagsFromHal(const std::vector<std::string>& strTags,
+                                     hidl_vec<AudioTag>* tags);
+    static status_t audioTagsToHal(const hidl_vec<AudioTag>& tags, char* halTags);
+    static hidl_vec<AudioTag> filterOutNonVendorTags(const hidl_vec<AudioTag>& tags);
+    static std::vector<std::string> filterOutNonVendorTags(const std::vector<std::string>& tags);
+    static std::vector<std::string> splitAudioTags(const char* halTags);
+    static status_t audioTransportsFromHal(const struct audio_port_v7& halPort, bool isInput,
+                                           hidl_vec<AudioTransport>* transports);
+    static status_t audioTransportsToHal(const hidl_vec<AudioTransport>& transports,
+                                         struct audio_port_v7* halTransport);
+
+  private:
+    static status_t audioIndexChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioInputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                 AudioChannelMask* channelMask);
+    static status_t audioOutputChannelMaskFromHal(audio_channel_mask_t halChannelMask,
+                                                  AudioChannelMask* channelMask);
+    static status_t audioPortExtendedInfoFromHal(
+            audio_port_role_t role, audio_port_type_t type,
+            const struct audio_port_config_device_ext& device,
+            const struct audio_port_config_mix_ext& mix,
+            const struct audio_port_config_session_ext& session, AudioPortExtendedInfo* ext,
+            bool* isInput);
+    static status_t audioPortExtendedInfoToHal(const AudioPortExtendedInfo& ext,
+                                               audio_port_role_t* role, audio_port_type_t* type,
+                                               struct audio_port_config_device_ext* device,
+                                               struct audio_port_config_mix_ext* mix,
+                                               struct audio_port_config_session_ext* session);
+    static status_t encapsulationTypeFromHal(audio_encapsulation_type_t halEncapsulationType,
+                                             AudioEncapsulationType* encapsulationType);
+    static status_t encapsulationTypeToHal(const AudioEncapsulationType& encapsulationType,
+                                           audio_encapsulation_type_t* halEncapsulationType);
+
+#endif  // MAJOR_VERSION >= 7
+
+    // V4 and below have DeviceAddress defined in the 'core' interface.
+    // To avoid duplicating code, the implementations of deviceAddressTo/FromHal
+    // are defined as templates. These templates can be only used directly by V4
+    // and below.
+#if MAJOR_VERSION >= 5
+  private:
+#endif
+    template <typename DA>
+    static status_t deviceAddressToHalImpl(const DA& device, audio_devices_t* halDeviceType,
+                                           char* halDeviceAddress);
+    template <typename DA>
+    static status_t deviceAddressFromHalImpl(audio_devices_t halDeviceType,
+                                             const char* halDeviceAddress, DA* device);
 };
 
+#if MAJOR_VERSION <= 6
+#if MAJOR_VERSION >= 4
+inline status_t HidlUtils::audioContentTypeFromHal(const audio_content_type_t halContentType,
+                                                   AudioContentType* contentType) {
+    *contentType = AudioContentType(halContentType);
+    return NO_ERROR;
+}
+
+inline status_t HidlUtils::audioContentTypeToHal(const AudioContentType& contentType,
+                                                 audio_content_type_t* halContentType) {
+    *halContentType = static_cast<audio_content_type_t>(contentType);
+    return NO_ERROR;
+}
+#endif
+
+inline status_t HidlUtils::audioSourceFromHal(audio_source_t halSource, AudioSource* source) {
+    *source = AudioSource(halSource);
+    return NO_ERROR;
+}
+
+inline status_t HidlUtils::audioSourceToHal(const AudioSource& source, audio_source_t* halSource) {
+    *halSource = static_cast<audio_source_t>(source);
+    return NO_ERROR;
+}
+
+template <typename DA>
+status_t HidlUtils::deviceAddressToHalImpl(const DA& device, audio_devices_t* halDeviceType,
+                                           char* halDeviceAddress) {
+    *halDeviceType = static_cast<audio_devices_t>(device.device);
+    memset(halDeviceAddress, 0, AUDIO_DEVICE_MAX_ADDRESS_LEN);
+    if (audio_is_a2dp_out_device(*halDeviceType) || audio_is_a2dp_in_device(*halDeviceType)) {
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%02X:%02X:%02X:%02X:%02X:%02X",
+                 device.address.mac[0], device.address.mac[1], device.address.mac[2],
+                 device.address.mac[3], device.address.mac[4], device.address.mac[5]);
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_IP || *halDeviceType == AUDIO_DEVICE_IN_IP) {
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%d.%d.%d.%d",
+                 device.address.ipv4[0], device.address.ipv4[1], device.address.ipv4[2],
+                 device.address.ipv4[3]);
+    } else if (audio_is_usb_out_device(*halDeviceType) || audio_is_usb_in_device(*halDeviceType)) {
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "card=%d;device=%d",
+                 device.address.alsa.card, device.address.alsa.device);
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_BUS || *halDeviceType == AUDIO_DEVICE_IN_BUS) {
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s", device.busAddress.c_str());
+    } else if (*halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+               *halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s",
+                 device.rSubmixAddress.c_str());
+    } else {
+        // Fall back to bus address for other device types, e.g. for microphones.
+        snprintf(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN, "%s", device.busAddress.c_str());
+    }
+    return NO_ERROR;
+}
+
+template <typename DA>
+status_t HidlUtils::deviceAddressFromHalImpl(audio_devices_t halDeviceType,
+                                             const char* halDeviceAddress, DA* device) {
+    if (device == nullptr) {
+        return BAD_VALUE;
+    }
+    device->device = AudioDevice(halDeviceType);
+    if (halDeviceAddress == nullptr ||
+        strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
+        return NO_ERROR;
+    }
+
+    if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+        int status =
+                sscanf(halDeviceAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &device->address.mac[0],
+                       &device->address.mac[1], &device->address.mac[2], &device->address.mac[3],
+                       &device->address.mac[4], &device->address.mac[5]);
+        return status == 6 ? OK : BAD_VALUE;
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+        int status = sscanf(halDeviceAddress, "%hhu.%hhu.%hhu.%hhu", &device->address.ipv4[0],
+                            &device->address.ipv4[1], &device->address.ipv4[2],
+                            &device->address.ipv4[3]);
+        return status == 4 ? OK : BAD_VALUE;
+    } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+        int status = sscanf(halDeviceAddress, "card=%d;device=%d", &device->address.alsa.card,
+                            &device->address.alsa.device);
+        return status == 2 ? OK : BAD_VALUE;
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_BUS || halDeviceType == AUDIO_DEVICE_IN_BUS) {
+        device->busAddress = halDeviceAddress;
+        return OK;
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+               halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+        device->rSubmixAddress = halDeviceAddress;
+        return OK;
+    }
+    // Fall back to bus address for other device types, e.g. for microphones.
+    device->busAddress = halDeviceAddress;
+    return NO_ERROR;
+}
+#endif  // MAJOR_VERSION <= 6
+
 }  // namespace implementation
 }  // namespace CPP_VERSION
 }  // namespace common
diff --git a/audio/common/all-versions/default/HidlUtilsCommon.cpp b/audio/common/all-versions/default/HidlUtilsCommon.cpp
new file mode 100644
index 0000000..d2da193
--- /dev/null
+++ b/audio/common/all-versions/default/HidlUtilsCommon.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "HidlUtils.h"
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+status_t HidlUtils::audioPortConfigsFromHal(unsigned int numHalConfigs,
+                                            const struct audio_port_config* halConfigs,
+                                            hidl_vec<AudioPortConfig>* configs) {
+    status_t result = NO_ERROR;
+    configs->resize(numHalConfigs);
+    for (unsigned int i = 0; i < numHalConfigs; ++i) {
+        if (status_t status = audioPortConfigFromHal(halConfigs[i], &(*configs)[i]);
+            status != NO_ERROR) {
+            result = status;
+        }
+    }
+    return result;
+}
+
+status_t HidlUtils::audioPortConfigsToHal(const hidl_vec<AudioPortConfig>& configs,
+                                          std::unique_ptr<audio_port_config[]>* halConfigs) {
+    status_t result = NO_ERROR;
+    halConfigs->reset(new audio_port_config[configs.size()]);
+    for (size_t i = 0; i < configs.size(); ++i) {
+        if (status_t status = audioPortConfigToHal(configs[i], &(*halConfigs)[i]);
+            status != NO_ERROR) {
+            result = status;
+        }
+    }
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/OWNERS b/audio/common/all-versions/default/OWNERS
index 6fdc97c..24071af 100644
--- a/audio/common/all-versions/default/OWNERS
+++ b/audio/common/all-versions/default/OWNERS
@@ -1,3 +1,2 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
diff --git a/audio/common/all-versions/default/TEST_MAPPING b/audio/common/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..c965113
--- /dev/null
+++ b/audio/common/all-versions/default/TEST_MAPPING
@@ -0,0 +1,10 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.common@6.0-util_tests"
+    },
+    {
+      "name": "android.hardware.audio.common@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/common/all-versions/default/UuidUtils.cpp b/audio/common/all-versions/default/UuidUtils.cpp
new file mode 100644
index 0000000..6c4c94d
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "UuidUtils.h"
+
+#include <common/all-versions/VersionUtils.h>
+#include <string.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+void UuidUtils::uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid) {
+    uuid->timeLow = halUuid.timeLow;
+    uuid->timeMid = halUuid.timeMid;
+    uuid->versionAndTimeHigh = halUuid.timeHiAndVersion;
+    uuid->variantAndClockSeqHigh = halUuid.clockSeq;
+    memcpy(uuid->node.data(), halUuid.node, uuid->node.size());
+}
+
+void UuidUtils::uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid) {
+    halUuid->timeLow = uuid.timeLow;
+    halUuid->timeMid = uuid.timeMid;
+    halUuid->timeHiAndVersion = uuid.versionAndTimeHigh;
+    halUuid->clockSeq = uuid.variantAndClockSeqHigh;
+    memcpy(halUuid->node, uuid.node.data(), uuid.node.size());
+}
+
+std::string UuidUtils::uuidToString(const audio_uuid_t& halUuid) {
+    char str[64];
+    snprintf(str, sizeof(str), "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", halUuid.timeLow,
+             halUuid.timeMid, halUuid.timeHiAndVersion, halUuid.clockSeq, halUuid.node[0],
+             halUuid.node[1], halUuid.node[2], halUuid.node[3], halUuid.node[4], halUuid.node[5]);
+    return str;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/common/all-versions/default/UuidUtils.h b/audio/common/all-versions/default/UuidUtils.h
new file mode 100644
index 0000000..cd04fb0
--- /dev/null
+++ b/audio/common/all-versions/default/UuidUtils.h
@@ -0,0 +1,51 @@
+/*
+ * 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_audio_Uuid_Utils_H_
+#define android_hardware_audio_Uuid_Utils_H_
+
+#include <string>
+
+// clang-format off
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include <system/audio.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace common {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+class UuidUtils {
+  public:
+    static void uuidFromHal(const audio_uuid_t& halUuid, Uuid* uuid);
+    static void uuidToHal(const Uuid& uuid, audio_uuid_t* halUuid);
+    static std::string uuidToString(const audio_uuid_t& halUuid);
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace common
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
+
+#endif  // android_hardware_audio_Uuid_Utils_H_
diff --git a/audio/common/all-versions/default/VersionUtils.h b/audio/common/all-versions/default/VersionUtils.h
index e7755b1..9bfca0c 100644
--- a/audio/common/all-versions/default/VersionUtils.h
+++ b/audio/common/all-versions/default/VersionUtils.h
@@ -31,7 +31,7 @@
 typedef common::CPP_VERSION::AudioChannelMask AudioChannelBitfield;
 typedef common::CPP_VERSION::AudioOutputFlag AudioOutputFlagBitfield;
 typedef common::CPP_VERSION::AudioInputFlag AudioInputFlagBitfield;
-#elif MAJOR_VERSION >= 4
+#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
 typedef hidl_bitfield<common::CPP_VERSION::AudioDevice> AudioDeviceBitfield;
 typedef hidl_bitfield<common::CPP_VERSION::AudioChannelMask> AudioChannelBitfield;
 typedef hidl_bitfield<common::CPP_VERSION::AudioOutputFlag> AudioOutputFlagBitfield;
diff --git a/audio/common/all-versions/default/service/Android.bp b/audio/common/all-versions/default/service/Android.bp
index 3e8b715..0d4775c6a 100644
--- a/audio/common/all-versions/default/service/Android.bp
+++ b/audio/common/all-versions/default/service/Android.bp
@@ -1,14 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.audio.service",
 
     init_rc: ["android.hardware.audio.service.rc"],
     relative_install_path: "hw",
     vendor: true,
-    // Only support 32 bit  as the binary must always be installed at the same
+    // Prefer 32 bit as the binary must always be installed at the same
     // location for init to start it and the build system does not support
     // having two binaries installable to the same location even if they are
     // not installed in the same build.
-    compile_multilib: "32",
+    compile_multilib: "prefer32",
     srcs: ["service.cpp"],
 
     cflags: [
diff --git a/audio/common/all-versions/default/service/service.cpp b/audio/common/all-versions/default/service/service.cpp
index 147d062..898c22d 100644
--- a/audio/common/all-versions/default/service/service.cpp
+++ b/audio/common/all-versions/default/service/service.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "audiohalservice"
 
+#include <signal.h>
 #include <string>
 #include <vector>
 
@@ -45,6 +46,8 @@
 }
 
 int main(int /* argc */, char* /* argv */ []) {
+    signal(SIGPIPE, SIG_IGN);
+
     ::android::ProcessState::initWithDriver("/dev/vndbinder");
     // start a threadpool for vndbinder interactions
     ::android::ProcessState::self()->startThreadPool();
@@ -63,17 +66,17 @@
     const std::vector<InterfacesList> mandatoryInterfaces = {
         {
             "Audio Core API",
+            "android.hardware.audio@7.0::IDevicesFactory",
             "android.hardware.audio@6.0::IDevicesFactory",
             "android.hardware.audio@5.0::IDevicesFactory",
             "android.hardware.audio@4.0::IDevicesFactory",
-            "android.hardware.audio@2.0::IDevicesFactory"
         },
         {
             "Audio Effect API",
+            "android.hardware.audio.effect@7.0::IEffectsFactory",
             "android.hardware.audio.effect@6.0::IEffectsFactory",
             "android.hardware.audio.effect@5.0::IEffectsFactory",
             "android.hardware.audio.effect@4.0::IEffectsFactory",
-            "android.hardware.audio.effect@2.0::IEffectsFactory",
         }
     };
 
@@ -87,7 +90,8 @@
         },
         {
             "Bluetooth Audio API",
-            "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory"
+            "android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory",
+            "android.hardware.bluetooth.audio@2.0::IBluetoothAudioProvidersFactory",
         },
         // remove the old HIDL when Bluetooth Audio Hal V2 has offloading supported
         {
diff --git a/audio/common/all-versions/default/tests/hidlutils6_tests.cpp b/audio/common/all-versions/default/tests/hidlutils6_tests.cpp
new file mode 100644
index 0000000..3a24e75
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils6_tests.cpp
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <system/audio.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
+// Not generated automatically because DeviceAddress contains
+// an union.
+//
+// operator== must be defined in the same namespace as the data type.
+namespace android::hardware::audio::common::CPP_VERSION {
+
+inline bool operator==(const DeviceAddress& lhs, const DeviceAddress& rhs) {
+    if (lhs.device != rhs.device) return false;
+    audio_devices_t halDeviceType = static_cast<audio_devices_t>(lhs.device);
+    if (audio_is_a2dp_out_device(halDeviceType) || audio_is_a2dp_in_device(halDeviceType)) {
+        return lhs.address.mac == rhs.address.mac;
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_IP || halDeviceType == AUDIO_DEVICE_IN_IP) {
+        return lhs.address.ipv4 == rhs.address.ipv4;
+    } else if (audio_is_usb_out_device(halDeviceType) || audio_is_usb_in_device(halDeviceType)) {
+        return lhs.address.alsa == rhs.address.alsa;
+    } else if (halDeviceType == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
+               halDeviceType == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
+        return lhs.rSubmixAddress == rhs.rSubmixAddress;
+    }
+    // busAddress field can be used for types other than bus, e.g. for microphones.
+    return lhs.busAddress == rhs.busAddress;
+}
+
+}  // namespace android::hardware::audio::common::CPP_VERSION
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+    DeviceAddress deviceBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+    EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils6, ConvertUniqueDeviceAddress) {
+    DeviceAddress speaker;
+    speaker.device = AudioDevice::OUT_SPEAKER;
+    ConvertDeviceAddress(speaker);
+
+    DeviceAddress micWithAddress;
+    micWithAddress.device = AudioDevice::IN_BUILTIN_MIC;
+    micWithAddress.busAddress = "bottom";
+    ConvertDeviceAddress(micWithAddress);
+}
+
+TEST(HidlUtils6, ConvertA2dpDeviceAddress) {
+    DeviceAddress a2dpSpeaker;
+    a2dpSpeaker.device = AudioDevice::OUT_BLUETOOTH_A2DP_SPEAKER;
+    a2dpSpeaker.address.mac = std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6};
+    ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils6, ConvertIpv4DeviceAddress) {
+    DeviceAddress ipv4;
+    ipv4.device = AudioDevice::OUT_IP;
+    ipv4.address.ipv4 = std::array<uint8_t, 4>{1, 2, 3, 4};
+    ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils6, ConvertUsbDeviceAddress) {
+    DeviceAddress usbHeadset;
+    usbHeadset.device = AudioDevice::OUT_USB_HEADSET;
+    usbHeadset.address.alsa = {1, 2};
+    ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils6, ConvertBusDeviceAddress) {
+    DeviceAddress bus;
+    bus.device = AudioDevice::OUT_BUS;
+    bus.busAddress = "bus_device";
+    ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils6, ConvertRSubmixDeviceAddress) {
+    DeviceAddress rSubmix;
+    rSubmix.device = AudioDevice::OUT_REMOTE_SUBMIX;
+    rSubmix.rSubmixAddress = AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS;
+    ConvertDeviceAddress(rSubmix);
+}
diff --git a/audio/common/all-versions/default/tests/hidlutils_tests.cpp b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
new file mode 100644
index 0000000..c9e6fac
--- /dev/null
+++ b/audio/common/all-versions/default/tests/hidlutils_tests.cpp
@@ -0,0 +1,1123 @@
+/*
+ * 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 <array>
+#include <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "HidlUtils_Test"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using ::android::hardware::hidl_vec;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask = AUDIO_CHANNEL_INVALID;
+static constexpr audio_content_type_t kInvalidHalContentType =
+        static_cast<audio_content_type_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_format_t kInvalidHalFormat = AUDIO_FORMAT_INVALID;
+static constexpr audio_gain_mode_t kInvalidHalGainMode =
+        static_cast<audio_gain_mode_t>(0xFFFFFFFFU);
+// AUDIO_SOURCE_INVALID is framework-only.
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(-1);
+// AUDIO_STREAM_DEFAULT is framework-only
+static constexpr audio_stream_type_t kInvalidHalStreamType = static_cast<audio_stream_type_t>(-2);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+static constexpr audio_encapsulation_type_t kInvalidEncapsulationType =
+        static_cast<audio_encapsulation_type_t>(0xFFFFFFFFU);
+static constexpr audio_standard_t kInvalidAudioStandard =
+        static_cast<audio_standard_t>(0xFFFFFFFFU);
+
+TEST(HidlUtils, ConvertInvalidChannelMask) {
+    AudioChannelMask invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskFromHal(kInvalidHalChannelMask,
+                                                            true /*isInput*/, &invalid));
+    audio_channel_mask_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("", &halInvalid));
+    // INVALID channel mask is not in XSD thus it's not allowed for transfer over HIDL.
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("AUDIO_CHANNEL_INVALID", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMaskToHal("random string", &halInvalid));
+}
+
+// Might move these to the audio_policy_configuration_V7_0-enums library
+// if there would be usages in the default wrapper code. In that case,
+// it would be better to reimplement these methods using a proper switch statement
+// over all known enum values.
+static bool isInputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_IN_") != std::string::npos;
+}
+
+static bool isOutputChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_OUT_") != std::string::npos;
+}
+
+static bool isIndexChannelMask(xsd::AudioChannelMask channelMask) {
+    return toString(channelMask).find("_CHANNEL_INDEX_") != std::string::npos;
+}
+
+TEST(HidlUtils, ConvertChannelMask) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        const AudioChannelMask channelMask = toString(enumVal);
+        audio_channel_mask_t halChannelMask, halChannelMaskBack;
+        AudioChannelMask channelMaskBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskToHal(channelMask, &halChannelMask))
+                << "Conversion of \"" << channelMask << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioChannelMask::AUDIO_CHANNEL_NONE,
+                  audio_channel_mask_is_valid(halChannelMask))
+                << "Validity of \"" << channelMask << "\" is not as expected";
+        if (bool isInput = isInputChannelMask(enumVal); isInput || isOutputChannelMask(enumVal)) {
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskFromHal(halChannelMask, isInput, &channelMaskBack))
+                    << "Conversion of " << (isInput ? "input" : "output") << " channel mask "
+                    << halChannelMask << " failed";
+            // Due to aliased values, the result of 'fromHal' might not be the same
+            // as 'channelMask', thus we need to compare the results of 'toHal' conversion instead.
+            EXPECT_EQ(NO_ERROR,
+                      HidlUtils::audioChannelMaskToHal(channelMaskBack, &halChannelMaskBack))
+                    << "Conversion of \"" << channelMaskBack << "\" failed";
+            EXPECT_EQ(halChannelMask, halChannelMaskBack);
+        } else if (isIndexChannelMask(enumVal) ||
+                   enumVal == xsd::AudioChannelMask::AUDIO_CHANNEL_NONE) {
+            // Conversions for indexed masks and "none" must not depend on the provided direction.
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(halChannelMask, true /*isInput*/,
+                                                                   &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as input channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+            EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMaskFromHal(
+                                        halChannelMask, false /*isInput*/, &channelMaskBack))
+                    << "Conversion of indexed / none channel mask " << halChannelMask
+                    << " failed (as output channel mask)";
+            EXPECT_EQ(channelMask, channelMaskBack);
+        } else {
+            FAIL() << "Unrecognized channel mask \"" << channelMask << "\"";
+        }
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidChannelMasksFromHal) {
+    std::vector<std::string> validAndInvalidChannelMasks = {
+            toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO), "random string", ""};
+    hidl_vec<AudioChannelMask> validChannelMask;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioChannelMasksFromHal(validAndInvalidChannelMasks, &validChannelMask));
+    EXPECT_EQ(1, validChannelMask.size());
+    EXPECT_EQ(validAndInvalidChannelMasks[0], validChannelMask[0]);
+
+    std::vector<std::string> invalidChannelMasks = {"random string", ""};
+    hidl_vec<AudioChannelMask> empty;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioChannelMasksFromHal(invalidChannelMasks, &empty));
+    EXPECT_EQ(0, empty.size());
+}
+
+TEST(HidlUtils, ConvertChannelMasksFromHal) {
+    std::vector<std::string> allHalChannelMasks;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioChannelMask>{}) {
+        allHalChannelMasks.push_back(toString(enumVal));
+    }
+    hidl_vec<AudioChannelMask> allChannelMasks;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioChannelMasksFromHal(allHalChannelMasks, &allChannelMasks));
+    EXPECT_EQ(allHalChannelMasks.size(), allChannelMasks.size());
+    for (size_t i = 0; i < allHalChannelMasks.size(); ++i) {
+        EXPECT_EQ(allHalChannelMasks[i], allChannelMasks[i]);
+    }
+}
+
+static AudioConfigBase generateValidConfigBase(bool isInput) {
+    AudioConfigBase configBase;
+    configBase.sampleRateHz = 44100;
+    configBase.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    configBase.channelMask = isInput ? toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_STEREO)
+                                     : toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    return configBase;
+}
+
+TEST(HidlUtils, ConvertInvalidConfigBase) {
+    AudioConfigBase invalid;
+    audio_config_base_t halInvalidChannelMask = AUDIO_CONFIG_BASE_INITIALIZER;
+    halInvalidChannelMask.channel_mask = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseFromHal(halInvalidChannelMask, false /*isInput*/,
+                                                           &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseFromHal(halInvalidChannelMask, true /*isInput*/, &invalid));
+    audio_config_base_t halInvalidFormat = AUDIO_CONFIG_BASE_INITIALIZER;
+    halInvalidFormat.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseFromHal(halInvalidFormat, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseFromHal(halInvalidFormat, true /*isInput*/, &invalid));
+
+    audio_config_base_t halInvalid;
+    AudioConfigBase invalidChannelMask = generateValidConfigBase(false /*isInput*/);
+    invalidChannelMask.channelMask = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalidChannelMask, &halInvalid));
+    AudioConfigBase invalidFormat = generateValidConfigBase(false /*isInput*/);
+    invalidFormat.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseToHal(invalidFormat, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigBaseDefault) {
+    audio_config_base_t halBaseDefault = AUDIO_CONFIG_BASE_INITIALIZER;
+    AudioConfigBase baseDefaultOut, baseDefaultIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseFromHal(halBaseDefault, false /*isInput*/,
+                                                          &baseDefaultOut));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseFromHal(halBaseDefault, true /*isInput*/, &baseDefaultIn));
+    EXPECT_EQ(baseDefaultOut, baseDefaultIn);
+    audio_config_base_t halBaseDefaultBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(baseDefaultOut, &halBaseDefaultBack));
+    EXPECT_EQ(halBaseDefault.sample_rate, halBaseDefaultBack.sample_rate);
+    EXPECT_EQ(halBaseDefault.channel_mask, halBaseDefaultBack.channel_mask);
+    EXPECT_EQ(halBaseDefault.format, halBaseDefaultBack.format);
+}
+
+TEST(HidlUtils, ConvertConfigBase) {
+    AudioConfigBase configBaseOut = generateValidConfigBase(false /*isInput*/);
+    audio_config_base_t halConfigBaseOut;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBaseOut, &halConfigBaseOut));
+    AudioConfigBase configBaseOutBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseFromHal(halConfigBaseOut, false /*isInput*/,
+                                                          &configBaseOutBack));
+    EXPECT_EQ(configBaseOut, configBaseOutBack);
+
+    AudioConfigBase configBaseIn = generateValidConfigBase(true /*isInput*/);
+    audio_config_base_t halConfigBaseIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseToHal(configBaseIn, &halConfigBaseIn));
+    AudioConfigBase configBaseInBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseFromHal(halConfigBaseIn, true /*isInput*/,
+                                                          &configBaseInBack));
+    EXPECT_EQ(configBaseIn, configBaseInBack);
+}
+
+TEST(HidlUtils, ConvertInvalidConfigBaseOptional) {
+    AudioConfigBaseOptional invalid;
+    audio_config_base_t halInvalidChannelMask = AUDIO_CONFIG_BASE_INITIALIZER;
+    halInvalidChannelMask.channel_mask = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidChannelMask, false /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, true /*channelMaskSpecified*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidChannelMask, true /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, true /*channelMaskSpecified*/, &invalid));
+    // Unspecified invalid values are ignored
+    AudioConfigBaseOptional unspecified;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidChannelMask, false /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &unspecified));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidChannelMask, true /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &unspecified));
+    audio_config_base_t halInvalidFormat = AUDIO_CONFIG_BASE_INITIALIZER;
+    halInvalidFormat.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidFormat, false /*isInput*/, true /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidFormat, true /*isInput*/, true /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &invalid));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidFormat, false /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &unspecified));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseOptionalFromHal(
+                      halInvalidFormat, true /*isInput*/, false /*formatSpecified*/,
+                      false /*sampleRateSpecified*/, false /*channelMaskSpecified*/, &unspecified));
+
+    audio_config_base_t halInvalid;
+    AudioConfigBaseOptional invalidChannelMask;
+    bool formatSpecified, sampleRateSpecified, channelMaskSpecified;
+    invalidChannelMask.channelMask.value("random string");
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigBaseOptionalToHal(
+                                 invalidChannelMask, &halInvalid, &formatSpecified,
+                                 &sampleRateSpecified, &channelMaskSpecified));
+    AudioConfigBaseOptional invalidFormat;
+    invalidFormat.format.value("random string");
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigBaseOptionalToHal(invalidFormat, &halInvalid, &formatSpecified,
+                                                      &sampleRateSpecified, &channelMaskSpecified));
+}
+
+TEST(HidlUtils, ConvertConfigBaseOptionalDefault) {
+    audio_config_base_t halBaseDefault = AUDIO_CONFIG_BASE_INITIALIZER;
+    AudioConfigBaseOptional baseDefaultUnspecOut, baseDefaultUnspecIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halBaseDefault, false /*isInput*/, false /*formatSpecified*/,
+                                false /*sampleRateSpecified*/, false /*channelMaskSpecified*/,
+                                &baseDefaultUnspecOut));
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halBaseDefault, true /*isInput*/, false /*formatSpecified*/,
+                                false /*sampleRateSpecified*/, false /*channelMaskSpecified*/,
+                                &baseDefaultUnspecIn));
+    EXPECT_EQ(baseDefaultUnspecOut, baseDefaultUnspecIn);
+    audio_config_base_t halBaseDefaultUnspecBack = AUDIO_CONFIG_BASE_INITIALIZER;
+    bool formatSpecified, sampleRateSpecified, channelMaskSpecified;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalToHal(
+                                baseDefaultUnspecOut, &halBaseDefaultUnspecBack, &formatSpecified,
+                                &sampleRateSpecified, &channelMaskSpecified));
+    EXPECT_FALSE(formatSpecified);
+    EXPECT_FALSE(sampleRateSpecified);
+    EXPECT_FALSE(channelMaskSpecified);
+    EXPECT_EQ(halBaseDefault.sample_rate, halBaseDefaultUnspecBack.sample_rate);
+    EXPECT_EQ(halBaseDefault.channel_mask, halBaseDefaultUnspecBack.channel_mask);
+    EXPECT_EQ(halBaseDefault.format, halBaseDefaultUnspecBack.format);
+
+    AudioConfigBaseOptional baseDefaultSpecOut, baseDefaultSpecIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halBaseDefault, false /*isInput*/, true /*formatSpecified*/,
+                                true /*sampleRateSpecified*/, true /*channelMaskSpecified*/,
+                                &baseDefaultSpecOut));
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halBaseDefault, true /*isInput*/, true /*formatSpecified*/,
+                                true /*sampleRateSpecified*/, true /*channelMaskSpecified*/,
+                                &baseDefaultSpecIn));
+    EXPECT_EQ(baseDefaultSpecOut, baseDefaultSpecIn);
+    audio_config_base_t halBaseDefaultSpecBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalToHal(
+                                baseDefaultSpecOut, &halBaseDefaultSpecBack, &formatSpecified,
+                                &sampleRateSpecified, &channelMaskSpecified));
+    EXPECT_TRUE(formatSpecified);
+    EXPECT_TRUE(sampleRateSpecified);
+    EXPECT_TRUE(channelMaskSpecified);
+    EXPECT_EQ(halBaseDefault.sample_rate, halBaseDefaultSpecBack.sample_rate);
+    EXPECT_EQ(halBaseDefault.channel_mask, halBaseDefaultSpecBack.channel_mask);
+    EXPECT_EQ(halBaseDefault.format, halBaseDefaultSpecBack.format);
+}
+
+TEST(HidlUtils, ConvertConfigBaseOptionalEmpty) {
+    AudioConfigBaseOptional empty;
+    bool formatSpecified, sampleRateSpecified, channelMaskSpecified;
+    audio_config_base_t halEmpty = AUDIO_CONFIG_BASE_INITIALIZER;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigBaseOptionalToHal(empty, &halEmpty, &formatSpecified,
+                                                      &sampleRateSpecified, &channelMaskSpecified));
+    EXPECT_FALSE(formatSpecified);
+    EXPECT_FALSE(sampleRateSpecified);
+    EXPECT_FALSE(channelMaskSpecified);
+    AudioConfigBaseOptional emptyOutBack, emptyInBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halEmpty, false /*isInput*/, formatSpecified, sampleRateSpecified,
+                                channelMaskSpecified, &emptyOutBack));
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halEmpty, true /*isInput*/, formatSpecified, sampleRateSpecified,
+                                channelMaskSpecified, &emptyInBack));
+    EXPECT_EQ(emptyOutBack, emptyInBack);
+    EXPECT_EQ(empty, emptyOutBack);
+}
+
+TEST(HidlUtils, ConvertConfigBaseOptional) {
+    AudioConfigBase validBaseOut = generateValidConfigBase(false /*isInput*/);
+    AudioConfigBaseOptional configBaseOut;
+    configBaseOut.format.value(validBaseOut.format);
+    configBaseOut.sampleRateHz.value(validBaseOut.sampleRateHz);
+    configBaseOut.channelMask.value(validBaseOut.channelMask);
+    audio_config_base_t halConfigBaseOut;
+    bool formatSpecified, sampleRateSpecified, channelMaskSpecified;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalToHal(
+                                configBaseOut, &halConfigBaseOut, &formatSpecified,
+                                &sampleRateSpecified, &channelMaskSpecified));
+    EXPECT_TRUE(formatSpecified);
+    EXPECT_TRUE(sampleRateSpecified);
+    EXPECT_TRUE(channelMaskSpecified);
+    AudioConfigBaseOptional configBaseOutBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halConfigBaseOut, false /*isInput*/, formatSpecified,
+                                sampleRateSpecified, channelMaskSpecified, &configBaseOutBack));
+    EXPECT_EQ(configBaseOut, configBaseOutBack);
+
+    AudioConfigBase validBaseIn = generateValidConfigBase(true /*isInput*/);
+    AudioConfigBaseOptional configBaseIn;
+    configBaseIn.format.value(validBaseIn.format);
+    configBaseIn.sampleRateHz.value(validBaseIn.sampleRateHz);
+    configBaseIn.channelMask.value(validBaseIn.channelMask);
+    audio_config_base_t halConfigBaseIn;
+    formatSpecified = false;
+    sampleRateSpecified = false;
+    channelMaskSpecified = false;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalToHal(
+                                configBaseIn, &halConfigBaseIn, &formatSpecified,
+                                &sampleRateSpecified, &channelMaskSpecified));
+    EXPECT_TRUE(formatSpecified);
+    EXPECT_TRUE(sampleRateSpecified);
+    EXPECT_TRUE(channelMaskSpecified);
+    AudioConfigBaseOptional configBaseInBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigBaseOptionalFromHal(
+                                halConfigBaseIn, true /*isInput*/, formatSpecified,
+                                sampleRateSpecified, channelMaskSpecified, &configBaseInBack));
+    EXPECT_EQ(configBaseIn, configBaseInBack);
+}
+
+TEST(HidlUtils, ConvertInvalidContentType) {
+    AudioContentType invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioContentTypeFromHal(kInvalidHalContentType, &invalid));
+    audio_content_type_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioContentTypeToHal("", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioContentTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertContentType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioContentType>{}) {
+        const AudioContentType contentType = toString(enumVal);
+        audio_content_type_t halContentType;
+        AudioContentType contentTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioContentTypeToHal(contentType, &halContentType))
+                << "Conversion of \"" << contentType << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioContentTypeFromHal(halContentType, &contentTypeBack))
+                << "Conversion of content type " << halContentType << " failed";
+        EXPECT_EQ(contentType, contentTypeBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidDeviceType) {
+    AudioDevice invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeFromHal(kInvalidHalDevice, &invalid));
+    audio_devices_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeToHal("", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioDeviceTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertDeviceType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioDevice>{}) {
+        const AudioDevice deviceType = toString(enumVal);
+        audio_devices_t halDeviceType, halDeviceTypeBack;
+        AudioDevice deviceTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceType, &halDeviceType))
+                << "Conversion of \"" << deviceType << "\" failed";
+        if (enumVal != xsd::AudioDevice::AUDIO_DEVICE_NONE) {
+            EXPECT_TRUE(audio_is_input_device(halDeviceType) ||
+                        audio_is_output_device(halDeviceType))
+                    << "Device \"" << deviceType << "\" is neither input, nor output device";
+        } else {
+            EXPECT_FALSE(audio_is_input_device(halDeviceType));
+            EXPECT_FALSE(audio_is_output_device(halDeviceType));
+        }
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeFromHal(halDeviceType, &deviceTypeBack))
+                << "Conversion of device type " << halDeviceType << " failed";
+        // Due to aliased values, the result of 'fromHal' might not be the same
+        // as 'deviceType', thus we need to compare the results of 'toHal' conversion instead.
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioDeviceTypeToHal(deviceTypeBack, &halDeviceTypeBack))
+                << "Conversion of \"" << deviceTypeBack << "\" failed";
+        EXPECT_EQ(halDeviceType, halDeviceTypeBack);
+    }
+}
+
+// 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) {
+    DeviceAddress invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER,
+                                                         "", &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_IP, "", &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, nullptr, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::deviceAddressFromHal(AUDIO_DEVICE_OUT_USB_HEADSET, "", &invalid));
+
+    audio_devices_t halInvalid;
+    char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    invalid = {};
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+    invalid.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::deviceAddressToHal(invalid, &halInvalid, halAddress));
+}
+
+static void ConvertDeviceAddress(const DeviceAddress& device) {
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress));
+    DeviceAddress deviceBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, &deviceBack));
+    EXPECT_EQ(device, deviceBack);
+}
+
+TEST(HidlUtils, ConvertUniqueDeviceAddress) {
+    DeviceAddress speaker;
+    speaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    ConvertDeviceAddress(speaker);
+
+    DeviceAddress micWithAddress;
+    micWithAddress.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC);
+    micWithAddress.address.id("bottom");
+    ConvertDeviceAddress(micWithAddress);
+}
+
+TEST(HidlUtils, ConvertA2dpDeviceAddress) {
+    DeviceAddress a2dpSpeaker;
+    a2dpSpeaker.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER);
+    a2dpSpeaker.address.mac(std::array<uint8_t, 6>{1, 2, 3, 4, 5, 6});
+    ConvertDeviceAddress(a2dpSpeaker);
+}
+
+TEST(HidlUtils, ConvertIpv4DeviceAddress) {
+    DeviceAddress ipv4;
+    ipv4.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_IP);
+    ipv4.address.ipv4(std::array<uint8_t, 4>{1, 2, 3, 4});
+    ConvertDeviceAddress(ipv4);
+}
+
+TEST(HidlUtils, ConvertUsbDeviceAddress) {
+    DeviceAddress usbHeadset;
+    usbHeadset.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_USB_HEADSET);
+    usbHeadset.address.alsa({1, 2});
+    ConvertDeviceAddress(usbHeadset);
+}
+
+TEST(HidlUtils, ConvertBusDeviceAddress) {
+    DeviceAddress bus;
+    bus.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_BUS);
+    bus.address.id("bus_device");
+    ConvertDeviceAddress(bus);
+}
+
+TEST(HidlUtils, ConvertRSubmixDeviceAddress) {
+    DeviceAddress rSubmix;
+    rSubmix.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_REMOTE_SUBMIX);
+    rSubmix.address.id(AUDIO_REMOTE_SUBMIX_DEVICE_ADDRESS);
+    ConvertDeviceAddress(rSubmix);
+}
+
+TEST(HidlUtils, ConvertVendorDeviceAddress) {
+    // The address part is not mandatory, both cases must work.
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strnlen(halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+    {
+        DeviceAddress vendor;
+        vendor.deviceType = "VX_GOOGLE_VR";
+        vendor.address.id("vr1");
+        audio_devices_t halDeviceType;
+        char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN] = {};
+        // Ignore the result. Vendors will also add the extended device into
+        // the list of devices in audio-hal-enums.h. Without that, the conversion
+        // officially fails, but it still maps the device type to NONE and converts
+        // the address.
+        (void)HidlUtils::deviceAddressToHal(vendor, &halDeviceType, halDeviceAddress);
+        EXPECT_EQ(AUDIO_DEVICE_NONE, halDeviceType);
+        EXPECT_EQ(0, strncmp("vr1", halDeviceAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN));
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidFormat) {
+    AudioFormat invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatFromHal(kInvalidHalFormat, &invalid));
+    audio_format_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("", &halInvalid));
+    // INVALID format is not in XSD thus it's not allowed for transfer over HIDL.
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("AUDIO_FORMAT_INVALID", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertFormat) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+        const AudioFormat format = toString(enumVal);
+        audio_format_t halFormat;
+        AudioFormat formatBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatToHal(format, &halFormat))
+                << "Conversion of \"" << format << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioFormat::AUDIO_FORMAT_DEFAULT,
+                  audio_is_valid_format(halFormat))
+                << "Validity of \"" << format << "\" is not as expected";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatFromHal(halFormat, &formatBack))
+                << "Conversion of format " << halFormat << " failed";
+        EXPECT_EQ(format, formatBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidFormatsFromHal) {
+    std::vector<std::string> validAndInvalidFormats = {
+            toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT), "random string", ""};
+    hidl_vec<AudioFormat> validFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatsFromHal(validAndInvalidFormats, &validFormat));
+    EXPECT_EQ(1, validFormat.size());
+    EXPECT_EQ(validAndInvalidFormats[0], validFormat[0]);
+
+    std::vector<std::string> invalidFormats = {"random string", ""};
+    hidl_vec<AudioFormat> empty;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioFormatsFromHal(invalidFormats, &empty));
+    EXPECT_EQ(0, empty.size());
+}
+
+TEST(HidlUtils, ConvertFormatsFromHal) {
+    std::vector<std::string> allHalFormats;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioFormat>{}) {
+        allHalFormats.push_back(toString(enumVal));
+    }
+    hidl_vec<AudioFormat> allFormats;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioFormatsFromHal(allHalFormats, &allFormats));
+    EXPECT_EQ(allHalFormats.size(), allFormats.size());
+    for (size_t i = 0; i < allHalFormats.size(); ++i) {
+        EXPECT_EQ(allHalFormats[i], allFormats[i]);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGainModeMask) {
+    hidl_vec<AudioGainMode> invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskFromHal(kInvalidHalGainMode, &invalid));
+    audio_gain_mode_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainModeMaskToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainModeMask) {
+    hidl_vec<AudioGainMode> emptyGainModes;
+    audio_gain_mode_t halEmptyGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(emptyGainModes, &halEmptyGainModes));
+    hidl_vec<AudioGainMode> emptyGainModesBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainModeMaskFromHal(halEmptyGainModes, &emptyGainModesBack));
+    EXPECT_EQ(emptyGainModes, emptyGainModesBack);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioGainMode>{}) {
+        allEnumValues.push_back(toString(enumVal));
+    }
+    hidl_vec<AudioGainMode> allGainModes;
+    allGainModes.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allGainModes[i] = allEnumValues[i];
+    }
+    audio_gain_mode_t halAllGainModes;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskToHal(allGainModes, &halAllGainModes));
+    hidl_vec<AudioGainMode> allGainModesBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainModeMaskFromHal(halAllGainModes, &allGainModesBack));
+    EXPECT_EQ(allGainModes, allGainModesBack);
+}
+
+TEST(HidlUtils, ConvertInvalidSource) {
+    AudioSource invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceFromHal(kInvalidHalSource, &invalid));
+    audio_source_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("", &halInvalid));
+    // INVALID source is not in XSD thus it's not allowed for transfer over HIDL.
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("AUDIO_SOURCE_INVALID", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioSourceToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertSource) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioSource>{}) {
+        const AudioSource source = toString(enumVal);
+        audio_source_t halSource;
+        AudioSource sourceBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceToHal(source, &halSource))
+                << "Conversion of \"" << source << "\" failed";
+        EXPECT_EQ(enumVal != xsd::AudioSource::AUDIO_SOURCE_DEFAULT,
+                  audio_is_valid_audio_source(halSource))
+                << "Validity of \"" << source << "\" is not as expected";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioSourceFromHal(halSource, &sourceBack))
+                << "Conversion of source " << halSource << " failed";
+        EXPECT_EQ(source, sourceBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidStreamType) {
+    AudioStreamType invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeFromHal(kInvalidHalStreamType, &invalid));
+    audio_stream_type_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioStreamTypeToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertDefaultStreamType) {
+    AudioStreamType streamDefault = "";
+    audio_stream_type_t halStreamDefault;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeToHal(streamDefault, &halStreamDefault));
+    AudioStreamType streamDefaultBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeFromHal(halStreamDefault, &streamDefaultBack));
+    EXPECT_EQ(streamDefault, streamDefaultBack);
+}
+
+TEST(HidlUtils, ConvertStreamType) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioStreamType>{}) {
+        const AudioStreamType streamType = toString(enumVal);
+        audio_stream_type_t halStreamType;
+        AudioStreamType streamTypeBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeToHal(streamType, &halStreamType))
+                << "Conversion of \"" << streamType << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioStreamTypeFromHal(halStreamType, &streamTypeBack))
+                << "Conversion of stream type " << halStreamType << " failed";
+        EXPECT_EQ(streamType, streamTypeBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidGain) {
+    AudioGain invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainFromHal({.mode = kInvalidHalGainMode},
+                                                     true /*isInput*/, &invalid));
+    struct audio_gain halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGain) {
+    AudioGain gain = {};
+    gain.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain halGain;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gain, &halGain));
+    AudioGain gainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainFromHal(halGain, false /*isInput*/, &gainBack));
+    EXPECT_EQ(gain, gainBack);
+    struct audio_gain halGainBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainToHal(gainBack, &halGainBack));
+    EXPECT_TRUE(audio_gains_are_equal(&halGain, &halGainBack));
+}
+
+TEST(HidlUtils, ConvertInvalidGainConfig) {
+    AudioGainConfig invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigFromHal({.mode = kInvalidHalGainMode},
+                                                           true /*isInput*/, &invalid));
+    struct audio_gain_config halInvalid;
+    invalid.mode.resize(1);
+    invalid.mode[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioGainConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertGainConfig) {
+    AudioGainConfig gainConfig = {};
+    gainConfig.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_gain_config halGainConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfig, &halGainConfig));
+    AudioGainConfig gainConfigBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioGainConfigFromHal(halGainConfig, false /*isInput*/, &gainConfigBack));
+    EXPECT_EQ(gainConfig, gainConfigBack);
+    struct audio_gain_config halGainConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioGainConfigToHal(gainConfigBack, &halGainConfigBack));
+    EXPECT_TRUE(audio_gain_config_are_equal(&halGainConfig, &halGainConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidUsage) {
+    AudioUsage invalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageFromHal(kInvalidHalUsage, &invalid));
+    audio_usage_t halInvalid;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageToHal("", &halInvalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioUsageToHal("random string", &halInvalid));
+}
+
+TEST(HidlUtils, ConvertUsage) {
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioUsage>{}) {
+        const AudioUsage usage = toString(enumVal);
+        audio_usage_t halUsage;
+        AudioUsage usageBack;
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageToHal(usage, &halUsage))
+                << "Conversion of \"" << usage << "\" failed";
+        EXPECT_EQ(NO_ERROR, HidlUtils::audioUsageFromHal(halUsage, &usageBack))
+                << "Conversion of usage " << halUsage << " failed";
+        EXPECT_EQ(usage, usageBack);
+    }
+}
+
+TEST(HidlUtils, ConvertInvalidOffloadInfo) {
+    AudioOffloadInfo invalid;
+    audio_offload_info_t halInvalid = AUDIO_INFO_INITIALIZER;
+    halInvalid.channel_mask = kInvalidHalChannelMask;
+    halInvalid.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoFromHal(halInvalid, &invalid));
+    invalid.base.channelMask = "random string";
+    invalid.base.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioOffloadInfoToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertOffloadInfo) {
+    AudioOffloadInfo offloadInfo = {};
+    offloadInfo.base = generateValidConfigBase(false /*isInput*/);
+    offloadInfo.streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC);
+    offloadInfo.bitRatePerSecond = 320;
+    offloadInfo.durationMicroseconds = -1;
+    offloadInfo.bitWidth = 16;
+    offloadInfo.bufferSize = 1024;
+    offloadInfo.usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA);
+    offloadInfo.encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM;
+    offloadInfo.contentId = 42;
+    offloadInfo.syncId = 13;
+    audio_offload_info_t halOffloadInfo;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoToHal(offloadInfo, &halOffloadInfo));
+    AudioOffloadInfo offloadInfoBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioOffloadInfoFromHal(halOffloadInfo, &offloadInfoBack));
+    EXPECT_EQ(offloadInfo, offloadInfoBack);
+}
+
+TEST(HidlUtils, ConvertInvalidConfig) {
+    AudioConfig invalid;
+    audio_config_t halInvalidChannelMask = AUDIO_CONFIG_INITIALIZER;
+    halInvalidChannelMask.channel_mask = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidChannelMask, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidChannelMask, true /*isInput*/, &invalid));
+    audio_config_t halInvalidFormat = AUDIO_CONFIG_INITIALIZER;
+    halInvalidFormat.format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidFormat, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioConfigFromHal(halInvalidFormat, true /*isInput*/, &invalid));
+
+    AudioConfig invalidChannelMask;
+    audio_config_t halInvalid;
+    invalidChannelMask.base.channelMask = "random string";
+    invalidChannelMask.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_DEFAULT);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalidChannelMask, &halInvalid));
+    AudioConfig invalidFormat;
+    invalidFormat.base.format = "random string";
+    invalidFormat.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioConfigToHal(invalidFormat, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertConfigDefault) {
+    audio_config_t halDefault = AUDIO_CONFIG_INITIALIZER;
+    AudioConfig defaultOut, defaultIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halDefault, false /*isInput*/, &defaultOut));
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halDefault, true /*isInput*/, &defaultIn));
+    EXPECT_EQ(defaultOut, defaultIn);
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(defaultOut, &halDefault));
+
+    // Note: empty channel mask and config are not valid values.
+    AudioConfig defaultCfg{};
+    defaultCfg.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    defaultCfg.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_DEFAULT);
+    audio_config_t halDefaultCfg;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(defaultCfg, &halDefaultCfg));
+    AudioConfig defaultCfgBackOut, defaultCfgBackIn;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halDefaultCfg, false /*isInput*/, &defaultCfgBackOut));
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halDefaultCfg, true /*isInput*/, &defaultCfgBackIn));
+    EXPECT_EQ(defaultCfgBackOut, defaultCfgBackIn);
+    EXPECT_EQ(defaultCfg, defaultCfgBackOut);
+}
+
+TEST(HidlUtils, ConvertConfig) {
+    AudioConfig configOut{};
+    configOut.base = generateValidConfigBase(false /*isInput*/);
+    audio_config_t halConfigOut;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(configOut, &halConfigOut));
+    AudioConfig configOutBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halConfigOut, false /*isInput*/, &configOutBack));
+    EXPECT_EQ(configOut, configOutBack);
+
+    AudioConfig configIn{};
+    configIn.base = generateValidConfigBase(true /*isInput*/);
+    audio_config_t halConfigIn;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(configIn, &halConfigIn));
+    AudioConfig configInBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioConfigFromHal(halConfigIn, true /*isInput*/, &configInBack));
+    EXPECT_EQ(configIn, configInBack);
+}
+
+TEST(HidlUtils, ConvertConfigWithOffloadInfo) {
+    AudioConfig config = {};
+    config.base = generateValidConfigBase(false /*isInput*/);
+    config.offloadInfo.info(
+            AudioOffloadInfo{.base = config.base,
+                             .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
+                             .bitRatePerSecond = 320,
+                             .durationMicroseconds = -1,
+                             .bitWidth = 16,
+                             .bufferSize = 1024,
+                             .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+                             .encapsulationMode = AudioEncapsulationMode::ELEMENTARY_STREAM,
+                             .contentId = 42,
+                             .syncId = 13});
+    audio_config_t halConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigToHal(config, &halConfig));
+    AudioConfig configBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, &configBack));
+    EXPECT_EQ(config, configBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioProfile) {
+    AudioProfile invalid;
+    struct audio_profile halInvalid = {};
+    halInvalid.format = kInvalidHalFormat;
+    halInvalid.num_sample_rates = 0;
+    halInvalid.num_channel_masks = 1;
+    halInvalid.channel_masks[0] = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileFromHal(halInvalid, true /*isInput*/, &invalid));
+    invalid.format = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioProfileToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioProfile) {
+    AudioProfile profile = {};
+    profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    profile.sampleRates.resize(2);
+    profile.sampleRates[0] = 44100;
+    profile.sampleRates[1] = 48000;
+    profile.channelMasks.resize(2);
+    profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    struct audio_profile halProfile;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioProfileToHal(profile, &halProfile));
+    AudioProfile profileBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioProfileFromHal(halProfile, false /*isInput*/, &profileBack));
+    EXPECT_EQ(profile, profileBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPortConfig) {
+    AudioPortConfig invalid;
+    struct audio_port_config halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.config_mask = AUDIO_PORT_CONFIG_CHANNEL_MASK;
+    halInvalid.channel_mask = kInvalidHalChannelMask;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigFromHal(halInvalid, &invalid));
+    invalid.base.channelMask.value("random string");
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortConfigToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPortConfig) {
+    AudioPortConfig config = {};
+    config.id = 42;
+    config.base.sampleRateHz.value(44100);
+    config.base.channelMask.value(toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO));
+    config.base.format.value(toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT));
+    config.gain.config({});
+    config.gain.config().channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    config.ext.device({});
+    config.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    struct audio_port_config halConfig;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(config, &halConfig));
+    AudioPortConfig configBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigFromHal(halConfig, &configBack));
+    EXPECT_EQ(config, configBack);
+    struct audio_port_config halConfigBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortConfigToHal(configBack, &halConfigBack));
+    EXPECT_TRUE(audio_port_configs_are_equal(&halConfig, &halConfigBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioTransports) {
+    hidl_vec<AudioTransport> invalid;
+    struct audio_port_v7 halInvalid = {};
+    halInvalid.num_audio_profiles = 1;
+    halInvalid.audio_profiles[0].format = kInvalidHalFormat;
+    halInvalid.audio_profiles[0].encapsulation_type = kInvalidEncapsulationType;
+    halInvalid.num_extra_audio_descriptors = 1;
+    halInvalid.extra_audio_descriptors[0].standard = kInvalidAudioStandard;
+    halInvalid.extra_audio_descriptors[0].descriptor_length = EXTRA_AUDIO_DESCRIPTOR_SIZE + 1;
+    EXPECT_EQ(BAD_VALUE,
+              HidlUtils::audioTransportsFromHal(halInvalid, false /*isInput*/, &invalid));
+    invalid.resize(2);
+    AudioProfile invalidProfile;
+    invalidProfile.format = "random string";
+    invalid[0].audioCapability.profile(invalidProfile);
+    invalid[0].encapsulationType = "random string";
+    invalid[0].audioCapability.edid(hidl_vec<uint8_t>(EXTRA_AUDIO_DESCRIPTOR_SIZE + 1));
+    invalid[1].encapsulationType = "random string";
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioTransportsToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioTransports) {
+    hidl_vec<AudioTransport> transports;
+    transports.resize(2);
+    AudioProfile profile;
+    profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    profile.sampleRates.resize(2);
+    profile.sampleRates[0] = 44100;
+    profile.sampleRates[1] = 48000;
+    profile.channelMasks.resize(2);
+    profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    transports[0].audioCapability.profile(profile);
+    hidl_vec<uint8_t> shortAudioDescriptor({0x11, 0x06, 0x01});
+    transports[0].encapsulationType =
+            toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_NONE);
+    transports[1].audioCapability.edid(std::move(shortAudioDescriptor));
+    transports[1].encapsulationType =
+            toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_IEC61937);
+    struct audio_port_v7 halPort;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioTransportsToHal(transports, &halPort));
+    hidl_vec<AudioTransport> transportsBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioTransportsFromHal(halPort, false /*isInput*/, &transportsBack));
+    EXPECT_EQ(transports, transportsBack);
+}
+
+TEST(HidlUtils, ConvertInvalidAudioPort) {
+    AudioPort invalid;
+    struct audio_port_v7 halInvalid = {};
+    halInvalid.type = AUDIO_PORT_TYPE_MIX;
+    halInvalid.role = AUDIO_PORT_ROLE_NONE;  // note: this is valid.
+    halInvalid.num_audio_profiles = 1;
+    halInvalid.audio_profiles[0].format = kInvalidHalFormat;
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortFromHal(halInvalid, &invalid));
+    invalid.transports.resize(1);
+    AudioProfile invalidProfile;
+    invalidProfile.format = "random string";
+    invalid.transports[0].audioCapability.profile(invalidProfile);
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioPortToHal(invalid, &halInvalid));
+}
+
+TEST(HidlUtils, ConvertAudioPort) {
+    AudioPort port = {};
+    port.id = 42;
+    port.name = "test";
+    port.transports.resize(2);
+    AudioProfile profile;
+    profile.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+    profile.sampleRates.resize(2);
+    profile.sampleRates[0] = 44100;
+    profile.sampleRates[1] = 48000;
+    profile.channelMasks.resize(2);
+    profile.channelMasks[0] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    profile.channelMasks[1] = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.transports[0].audioCapability.profile(profile);
+    port.transports[0].encapsulationType =
+            toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_NONE);
+    hidl_vec<uint8_t> shortAudioDescriptor({0x11, 0x06, 0x01});
+    port.transports[1].audioCapability.edid(std::move(shortAudioDescriptor));
+    port.transports[1].encapsulationType =
+            toString(xsd::AudioEncapsulationType::AUDIO_ENCAPSULATION_TYPE_IEC61937);
+    port.gains.resize(1);
+    port.gains[0].channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO);
+    port.ext.device({});
+    port.ext.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    // active config left unspecified.
+    struct audio_port_v7 halPort;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(port, &halPort));
+    AudioPort portBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortFromHal(halPort, &portBack));
+    EXPECT_EQ(port, portBack);
+    struct audio_port_v7 halPortBack;
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioPortToHal(portBack, &halPortBack));
+    EXPECT_TRUE(audio_ports_v7_are_equal(&halPort, &halPortBack));
+}
+
+TEST(HidlUtils, ConvertInvalidAudioTags) {
+    char halTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+
+    hidl_vec<AudioTag> emptyTag = {{""}};
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(emptyTag, halTag));
+
+    hidl_vec<AudioTag> longTag = {{std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE + 1, 'A')}};
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(longTag, halTag));
+
+    hidl_vec<AudioTag> tagSeparator = {
+            {std::string(AUDIO_ATTRIBUTES_TAGS_MAX_SIZE - 1, HidlUtils::sAudioTagSeparator)}};
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(tagSeparator, halTag));
+
+    hidl_vec<AudioTag> notExtensions = {{"", "random string", "VX_", "VX_GOOGLE_$$"}};
+    EXPECT_EQ(BAD_VALUE, HidlUtils::audioTagsToHal(notExtensions, halTag));
+}
+
+TEST(HidlUtils, ConvertAudioTags) {
+    hidl_vec<AudioTag> emptyTags;
+    char halEmptyTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(emptyTags, halEmptyTags));
+    hidl_vec<AudioTag> emptyTagsBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(halEmptyTags), &emptyTagsBack));
+    EXPECT_EQ(emptyTags, emptyTagsBack);
+
+    hidl_vec<AudioTag> oneTag = {{"VX_GOOGLE_VR"}};
+    char halOneTag[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(oneTag, halOneTag));
+    hidl_vec<AudioTag> oneTagBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(halOneTag), &oneTagBack));
+    EXPECT_EQ(oneTag, oneTagBack);
+
+    hidl_vec<AudioTag> twoTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+    char halTwoTags[AUDIO_ATTRIBUTES_TAGS_MAX_SIZE] = {};
+    EXPECT_EQ(NO_ERROR, HidlUtils::audioTagsToHal(twoTags, halTwoTags));
+    hidl_vec<AudioTag> twoTagsBack;
+    EXPECT_EQ(NO_ERROR,
+              HidlUtils::audioTagsFromHal(HidlUtils::splitAudioTags(halTwoTags), &twoTagsBack));
+    EXPECT_EQ(twoTags, twoTagsBack);
+}
+
+template <typename T>
+class FilterTest : public ::testing::Test {};
+using FilterTestTypeParams = ::testing::Types<hidl_vec<AudioTag>, std::vector<std::string>>;
+TYPED_TEST_SUITE(FilterTest, FilterTestTypeParams);
+
+TYPED_TEST(FilterTest, FilterOutNonVendorTags) {
+    TypeParam emptyTags;
+    EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(emptyTags));
+
+    TypeParam allVendorTags = {{"VX_GOOGLE_VR_42", "VX_GOOGLE_1E100"}};
+    EXPECT_EQ(allVendorTags, HidlUtils::filterOutNonVendorTags(allVendorTags));
+
+    TypeParam oneVendorTag = {{"", "VX_GOOGLE_VR", "random_string"}};
+    TypeParam oneVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneVendorTag);
+    EXPECT_EQ(1, oneVendorTagOnly.size());
+    EXPECT_EQ(oneVendorTag[1], oneVendorTagOnly[0]);
+
+    // The vendor extension isn't valid, however it must not be filtered out
+    // so the converter can detect the issue.
+    TypeParam oneMaybeVendorTag = {{"", "random string", "VX_GOOGLE_$$"}};
+    TypeParam oneMaybeVendorTagOnly = HidlUtils::filterOutNonVendorTags(oneMaybeVendorTag);
+    EXPECT_EQ(1, oneMaybeVendorTagOnly.size());
+    EXPECT_EQ(oneMaybeVendorTag[2], oneMaybeVendorTagOnly[0]);
+
+    TypeParam noVendorTags = {{"", "random string", "V_"}};
+    EXPECT_EQ(emptyTags, HidlUtils::filterOutNonVendorTags(noVendorTags));
+}
diff --git a/audio/common/all-versions/test/OWNERS b/audio/common/all-versions/test/OWNERS
deleted file mode 100644
index 6a26ae7..0000000
--- a/audio/common/all-versions/test/OWNERS
+++ /dev/null
@@ -1,2 +0,0 @@
-yim@google.com
-zhuoyao@google.com
diff --git a/audio/common/all-versions/test/utility/Android.bp b/audio/common/all-versions/test/utility/Android.bp
index b796acc..757f8a8 100644
--- a/audio/common/all-versions/test/utility/Android.bp
+++ b/audio/common/all-versions/test/utility/Android.bp
@@ -14,9 +14,18 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.audio.common.test.utility",
-    defaults : ["hidl_defaults"],
+    defaults: ["hidl_defaults"],
     srcs: ["src/ValidateXml.cpp"],
     cflags: [
         "-O0",
@@ -25,8 +34,34 @@
     ],
     local_include_dirs: ["include/utility"],
     export_include_dirs: ["include"],
-    shared_libs: ["libxml2", "liblog"],
+    shared_libs: [
+        "libxml2",
+        "liblog",
+    ],
     static_libs: ["libgtest"],
     export_static_lib_headers: ["libgtest"],
 }
 
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of test utilities.
+cc_test {
+    name: "android.hardware.audio.common.test.utility_tests",
+    host_supported: true,
+    local_include_dirs: ["include/utility"],
+    srcs: [
+        "src/ValidateXml.cpp",
+        "tests/utility_tests.cpp",
+    ],
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-g",
+    ],
+    shared_libs: [
+        "libbase",
+        "libxml2",
+        "liblog",
+    ],
+    static_libs: ["libgtest"],
+    test_suites: ["general-tests"],
+}
diff --git a/audio/common/all-versions/test/utility/TEST_MAPPING b/audio/common/all-versions/test/utility/TEST_MAPPING
new file mode 100644
index 0000000..0bc1871
--- /dev/null
+++ b/audio/common/all-versions/test/utility/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.common.test.utility_tests"
+    }
+  ]
+}
diff --git a/audio/common/all-versions/test/utility/src/ValidateXml.cpp b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
index a866104..f111c01 100644
--- a/audio/common/all-versions/test/utility/src/ValidateXml.cpp
+++ b/audio/common/all-versions/test/utility/src/ValidateXml.cpp
@@ -112,7 +112,8 @@
         return ::testing::AssertionFailure() << "Failed to parse xml\n" << context();
     }
 
-    if (xmlXIncludeProcess(doc.get()) == -1) {
+    // Process 'include' directives w/o modifying elements loaded from included files.
+    if (xmlXIncludeProcessFlags(doc.get(), XML_PARSE_NOBASEFIX) == -1) {
         return ::testing::AssertionFailure() << "Failed to resolve xincludes in xml\n" << context();
     }
 
diff --git a/audio/common/all-versions/test/utility/tests/utility_tests.cpp b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
new file mode 100644
index 0000000..c523066
--- /dev/null
+++ b/audio/common/all-versions/test/utility/tests/utility_tests.cpp
@@ -0,0 +1,176 @@
+/*
+ * 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.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include <ValidateXml.h>
+
+using ::android::hardware::audio::common::test::utility::validateXml;
+
+const char* XSD_SOURCE =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+        "<xs:schema version=\"2.0\""
+        "           elementFormDefault=\"qualified\""
+        "           attributeFormDefault=\"unqualified\""
+        "           xmlns:xs=\"http://www.w3.org/2001/XMLSchema\">"
+        "  <xs:element name=\"audioPolicyConfiguration\">"
+        "    <xs:complexType>"
+        "      <xs:sequence>"
+        "        <xs:element name=\"modules\">"
+        "          <xs:complexType>"
+        "            <xs:sequence>"
+        "              <xs:element name=\"module\" maxOccurs=\"unbounded\">"
+        "                <xs:complexType>"
+        "                  <xs:attribute name=\"name\" type=\"xs:string\" use=\"required\"/>"
+        "                </xs:complexType>"
+        "              </xs:element>"
+        "            </xs:sequence>"
+        "          </xs:complexType>"
+        "        </xs:element>"
+        "      </xs:sequence>"
+        "    </xs:complexType>"
+        "  </xs:element>"
+        "</xs:schema>";
+
+const char* INVALID_XML_SOURCE =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+        "<audioPolicyKonfiguration />";
+
+const char* VALID_XML_SOURCE =
+        "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
+        "<audioPolicyConfiguration>"
+        "  <modules>"
+        "    <module name=\"aaa\" />"
+        "    %s"
+        "  </modules>"
+        "</audioPolicyConfiguration>";
+
+const char* MODULE_SOURCE = "<module name=\"bbb\" />";
+
+const char* XI_INCLUDE = "<xi:include xmlns:xi=\"http://www.w3.org/2001/XInclude\" href=\"%s\" />";
+
+const char* XML_INCLUDED_SOURCE = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>%s";
+
+namespace {
+
+std::string substitute(const char* fmt, const char* param) {
+    std::string buffer(static_cast<size_t>(strlen(fmt) + strlen(param)), '\0');
+    snprintf(buffer.data(), buffer.size(), fmt, param);
+    return buffer;
+}
+
+std::string substitute(const char* fmt, const std::string& s) {
+    return substitute(fmt, s.c_str());
+}
+
+}  // namespace
+
+TEST(ValidateXml, InvalidXml) {
+    TemporaryFile xml;
+    ASSERT_TRUE(android::base::WriteStringToFile(INVALID_XML_SOURCE, xml.path)) << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_FALSE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, ValidXml) {
+    TemporaryFile xml;
+    ASSERT_TRUE(
+            android::base::WriteStringToFile(substitute(VALID_XML_SOURCE, MODULE_SOURCE), xml.path))
+            << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeAbsolutePath) {
+    TemporaryFile xmlInclude;
+    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+                                                 xmlInclude.path))
+            << strerror(errno);
+    TemporaryFile xml;
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            substitute(VALID_XML_SOURCE, substitute(XI_INCLUDE, xmlInclude.path)), xml.path))
+            << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeSameDirRelativePath) {
+    TemporaryFile xmlInclude;
+    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+                                                 xmlInclude.path))
+            << strerror(errno);
+    TemporaryFile xml;
+    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            substitute(VALID_XML_SOURCE,
+                       substitute(XI_INCLUDE, android::base::Basename(xmlInclude.path))),
+            xml.path))
+            << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeSubdirRelativePath) {
+    TemporaryDir xmlIncludeDir;
+    TemporaryFile xmlInclude(xmlIncludeDir.path);
+    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+                                                 xmlInclude.path))
+            << strerror(errno);
+    TemporaryFile xml;
+    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            substitute(VALID_XML_SOURCE,
+                       substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
+                                                      android::base::Basename(xmlInclude.path))),
+            xml.path))
+            << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
+
+TEST(ValidateXml, IncludeParentDirRelativePath) {
+    // An XML file from a subdirectory includes a file from the parent directory using '..' syntax.
+    TemporaryFile xmlInclude;
+    ASSERT_TRUE(android::base::WriteStringToFile(substitute(XML_INCLUDED_SOURCE, MODULE_SOURCE),
+                                                 xmlInclude.path))
+            << strerror(errno);
+    TemporaryDir xmlIncludeDir;
+    TemporaryFile xmlParentInclude(xmlIncludeDir.path);
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            substitute(XML_INCLUDED_SOURCE,
+                       substitute(XI_INCLUDE, "../" + android::base::Basename(xmlInclude.path))),
+            xmlParentInclude.path))
+            << strerror(errno);
+    TemporaryFile xml;
+    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlInclude.path));
+    ASSERT_EQ(android::base::Dirname(xml.path), android::base::Dirname(xmlIncludeDir.path));
+    ASSERT_TRUE(android::base::WriteStringToFile(
+            substitute(
+                    VALID_XML_SOURCE,
+                    substitute(XI_INCLUDE, android::base::Basename(xmlIncludeDir.path) + "/" +
+                                                   android::base::Basename(xmlParentInclude.path))),
+            xml.path))
+            << strerror(errno);
+    TemporaryFile xsd;
+    ASSERT_TRUE(android::base::WriteStringToFile(XSD_SOURCE, xsd.path)) << strerror(errno);
+    EXPECT_TRUE(validateXml("xml", "xsd", xml.path, xsd.path));
+}
diff --git a/audio/common/all-versions/util/Android.bp b/audio/common/all-versions/util/Android.bp
index 3c7e62e..91de6ec 100644
--- a/audio/common/all-versions/util/Android.bp
+++ b/audio/common/all-versions/util/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.audio.common.util@all-versions",
     defaults: ["hidl_defaults"],
diff --git a/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h b/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
index b514a43..d7802cf 100644
--- a/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
+++ b/audio/common/all-versions/util/include/common/all-versions/HidlSupport.h
@@ -20,6 +20,9 @@
 
 #include <hidl/HidlSupport.h>
 #include <algorithm>
+#include <sstream>
+#include <string>
+#include <vector>
 
 namespace android::hardware::audio::common::utils {
 
@@ -29,6 +32,16 @@
     return std::find(values.begin(), values.end(), e) != values.end();
 }
 
+static inline std::vector<std::string> splitString(const std::string& s, char separator) {
+    std::istringstream iss(s);
+    std::string t;
+    std::vector<std::string> result;
+    while (std::getline(iss, t, separator)) {
+        result.push_back(std::move(t));
+    }
+    return result;
+}
+
 } // namespace android::hardware::audio::common::utils
 
 #endif  // android_hardware_audio_common_HidlSupport_H_
diff --git a/audio/core/all-versions/default/OWNERS b/audio/core/all-versions/OWNERS
similarity index 100%
rename from audio/core/all-versions/default/OWNERS
rename to audio/core/all-versions/OWNERS
diff --git a/audio/core/all-versions/default/Android.bp b/audio/core/all-versions/default/Android.bp
index 0af81b2..615655d 100644
--- a/audio/core/all-versions/default/Android.bp
+++ b/audio/core/all-versions/default/Android.bp
@@ -1,10 +1,15 @@
-cc_defaults {
-    name: "android.hardware.audio-impl_default",
-    relative_install_path: "hw",
-    proprietary: true,
-    vendor: true,
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+filegroup {
+    name: "android.hardware.audio-impl_srcs",
     srcs: [
-        "Conversions.cpp",
         "Device.cpp",
         "DevicesFactory.cpp",
         "ParametersUtil.cpp",
@@ -13,11 +18,24 @@
         "StreamIn.cpp",
         "StreamOut.cpp",
     ],
+}
+
+cc_library_headers {
+    name: "android.hardware.audio-impl_headers",
+    proprietary: true,
+    vendor: true,
+    export_include_dirs: ["include"],
+}
+
+cc_defaults {
+    name: "android.hardware.audio-impl_default",
+    relative_install_path: "hw",
+    proprietary: true,
+    vendor: true,
+    srcs: [":android.hardware.audio-impl_srcs"],
 
     defaults: ["hidl_defaults"],
 
-    export_include_dirs: ["include"],
-
     static_libs: [
         "libaudiofoundation",
     ],
@@ -35,12 +53,18 @@
     ],
 
     header_libs: [
+        "android.hardware.audio-impl_headers",
         "android.hardware.audio.common.util@all-versions",
         "libaudioclient_headers",
+        "libaudioutils_headers",
         "libaudio_system_headers",
         "libhardware_headers",
         "libmedia_headers",
     ],
+
+    export_header_lib_headers: [
+        "android.hardware.audio-impl_headers",
+    ],
 }
 
 cc_library_shared {
@@ -48,6 +72,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@2.0",
+        "android.hardware.audio@2.0-util",
         "android.hardware.audio.common@2.0",
         "android.hardware.audio.common@2.0-util",
     ],
@@ -64,6 +89,7 @@
 
     shared_libs: [
         "android.hardware.audio@4.0",
+        "android.hardware.audio@4.0-util",
         "android.hardware.audio.common@4.0",
         "android.hardware.audio.common@4.0-util",
     ],
@@ -79,6 +105,7 @@
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@5.0",
+        "android.hardware.audio@5.0-util",
         "android.hardware.audio.common@5.0",
         "android.hardware.audio.common@5.0-util",
     ],
@@ -89,11 +116,12 @@
     ],
 }
 
-cc_library_shared {
-    name: "android.hardware.audio@6.0-impl",
+cc_defaults {
+    name: "android.hardware.audio@6.0-impl_default",
     defaults: ["android.hardware.audio-impl_default"],
     shared_libs: [
         "android.hardware.audio@6.0",
+        "android.hardware.audio@6.0-util",
         "android.hardware.audio.common@6.0",
         "android.hardware.audio.common@6.0-util",
     ],
@@ -103,3 +131,26 @@
         "-include common/all-versions/VersionMacro.h",
     ],
 }
+
+cc_library_shared {
+    name: "android.hardware.audio@6.0-impl",
+    defaults: ["android.hardware.audio@6.0-impl_default"],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@7.0-impl",
+    defaults: ["android.hardware.audio-impl_default"],
+    shared_libs: [
+        "android.hardware.audio@7.0",
+        "android.hardware.audio@7.0-util",
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "libbase",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
diff --git a/audio/core/all-versions/default/Conversions.cpp b/audio/core/all-versions/default/Conversions.cpp
deleted file mode 100644
index eddff55..0000000
--- a/audio/core/all-versions/default/Conversions.cpp
+++ /dev/null
@@ -1,185 +0,0 @@
-/*
- * Copyright (C) 2018 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 "core/default/Conversions.h"
-
-#include <stdio.h>
-
-#include <log/log.h>
-#include <media/AudioContainers.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-std::string deviceAddressToHal(const DeviceAddress& address) {
-    // HAL assumes that the address is NUL-terminated.
-    char halAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
-    memset(halAddress, 0, sizeof(halAddress));
-    uint32_t halDevice = static_cast<uint32_t>(address.device);
-    if (getAudioDeviceOutAllA2dpSet().count(halDevice) > 0 ||
-        halDevice == AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
-        snprintf(halAddress, sizeof(halAddress), "%02X:%02X:%02X:%02X:%02X:%02X",
-                 address.address.mac[0], address.address.mac[1], address.address.mac[2],
-                 address.address.mac[3], address.address.mac[4], address.address.mac[5]);
-    } else if (halDevice == AUDIO_DEVICE_OUT_IP || halDevice == AUDIO_DEVICE_IN_IP) {
-        snprintf(halAddress, sizeof(halAddress), "%d.%d.%d.%d", address.address.ipv4[0],
-                 address.address.ipv4[1], address.address.ipv4[2], address.address.ipv4[3]);
-    } else if (getAudioDeviceOutAllUsbSet().count(halDevice) > 0 ||
-               getAudioDeviceInAllUsbSet().count(halDevice) > 0) {
-        snprintf(halAddress, sizeof(halAddress), "card=%d;device=%d", address.address.alsa.card,
-                 address.address.alsa.device);
-    } else if (halDevice == AUDIO_DEVICE_OUT_BUS || halDevice == AUDIO_DEVICE_IN_BUS) {
-        snprintf(halAddress, sizeof(halAddress), "%s", address.busAddress.c_str());
-    } else if (halDevice == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
-               halDevice == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
-        snprintf(halAddress, sizeof(halAddress), "%s", address.rSubmixAddress.c_str());
-    }
-    return halAddress;
-}
-
-#if MAJOR_VERSION >= 4
-status_t deviceAddressFromHal(audio_devices_t device, const char* halAddress,
-                              DeviceAddress* address) {
-    if (address == nullptr) {
-        return BAD_VALUE;
-    }
-    address->device = AudioDevice(device);
-    if (halAddress == nullptr || strnlen(halAddress, AUDIO_DEVICE_MAX_ADDRESS_LEN) == 0) {
-        return OK;
-    }
-
-    if (getAudioDeviceOutAllA2dpSet().count(device) > 0 ||
-        device == AUDIO_DEVICE_IN_BLUETOOTH_A2DP) {
-        int status =
-            sscanf(halAddress, "%hhX:%hhX:%hhX:%hhX:%hhX:%hhX", &address->address.mac[0],
-                   &address->address.mac[1], &address->address.mac[2], &address->address.mac[3],
-                   &address->address.mac[4], &address->address.mac[5]);
-        return status == 6 ? OK : BAD_VALUE;
-    } else if (device == AUDIO_DEVICE_OUT_IP || device == AUDIO_DEVICE_IN_IP) {
-        int status =
-            sscanf(halAddress, "%hhu.%hhu.%hhu.%hhu", &address->address.ipv4[0],
-                   &address->address.ipv4[1], &address->address.ipv4[2], &address->address.ipv4[3]);
-        return status == 4 ? OK : BAD_VALUE;
-    } else if (getAudioDeviceOutAllUsbSet().count(device) > 0 ||
-               getAudioDeviceInAllUsbSet().count(device) > 0) {
-        int status = sscanf(halAddress, "card=%d;device=%d", &address->address.alsa.card,
-                            &address->address.alsa.device);
-        return status == 2 ? OK : BAD_VALUE;
-    } else if (device == AUDIO_DEVICE_OUT_BUS || device == AUDIO_DEVICE_IN_BUS) {
-        address->busAddress = halAddress;
-        return OK;
-    } else if (device == AUDIO_DEVICE_OUT_REMOTE_SUBMIX ||
-               device == AUDIO_DEVICE_IN_REMOTE_SUBMIX) {
-        address->rSubmixAddress = halAddress;
-        return OK;
-    }
-    address->busAddress = halAddress;
-    return OK;
-}
-
-AudioMicrophoneChannelMapping halToChannelMapping(audio_microphone_channel_mapping_t mapping) {
-    switch (mapping) {
-        case AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED:
-            return AudioMicrophoneChannelMapping::UNUSED;
-        case AUDIO_MICROPHONE_CHANNEL_MAPPING_DIRECT:
-            return AudioMicrophoneChannelMapping::DIRECT;
-        case AUDIO_MICROPHONE_CHANNEL_MAPPING_PROCESSED:
-            return AudioMicrophoneChannelMapping::PROCESSED;
-        default:
-            ALOGE("Invalid channel mapping type: %d", mapping);
-            return AudioMicrophoneChannelMapping::UNUSED;
-    }
-}
-
-AudioMicrophoneLocation halToLocation(audio_microphone_location_t location) {
-    switch (location) {
-        default:
-        case AUDIO_MICROPHONE_LOCATION_UNKNOWN:
-            return AudioMicrophoneLocation::UNKNOWN;
-        case AUDIO_MICROPHONE_LOCATION_MAINBODY:
-            return AudioMicrophoneLocation::MAINBODY;
-        case AUDIO_MICROPHONE_LOCATION_MAINBODY_MOVABLE:
-            return AudioMicrophoneLocation::MAINBODY_MOVABLE;
-        case AUDIO_MICROPHONE_LOCATION_PERIPHERAL:
-            return AudioMicrophoneLocation::PERIPHERAL;
-    }
-}
-
-AudioMicrophoneDirectionality halToDirectionality(audio_microphone_directionality_t dir) {
-    switch (dir) {
-        default:
-        case AUDIO_MICROPHONE_DIRECTIONALITY_UNKNOWN:
-            return AudioMicrophoneDirectionality::UNKNOWN;
-        case AUDIO_MICROPHONE_DIRECTIONALITY_OMNI:
-            return AudioMicrophoneDirectionality::OMNI;
-        case AUDIO_MICROPHONE_DIRECTIONALITY_BI_DIRECTIONAL:
-            return AudioMicrophoneDirectionality::BI_DIRECTIONAL;
-        case AUDIO_MICROPHONE_DIRECTIONALITY_CARDIOID:
-            return AudioMicrophoneDirectionality::CARDIOID;
-        case AUDIO_MICROPHONE_DIRECTIONALITY_HYPER_CARDIOID:
-            return AudioMicrophoneDirectionality::HYPER_CARDIOID;
-        case AUDIO_MICROPHONE_DIRECTIONALITY_SUPER_CARDIOID:
-            return AudioMicrophoneDirectionality::SUPER_CARDIOID;
-    }
-}
-
-bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
-                                    const struct audio_microphone_characteristic_t& src) {
-    bool status = false;
-    if (pDst != NULL) {
-        pDst->deviceId = src.device_id;
-
-        if (deviceAddressFromHal(src.device, src.address, &pDst->deviceAddress) != OK) {
-            return false;
-        }
-        pDst->channelMapping.resize(AUDIO_CHANNEL_COUNT_MAX);
-        for (size_t ch = 0; ch < pDst->channelMapping.size(); ch++) {
-            pDst->channelMapping[ch] = halToChannelMapping(src.channel_mapping[ch]);
-        }
-        pDst->location = halToLocation(src.location);
-        pDst->group = (AudioMicrophoneGroup)src.group;
-        pDst->indexInTheGroup = (uint32_t)src.index_in_the_group;
-        pDst->sensitivity = src.sensitivity;
-        pDst->maxSpl = src.max_spl;
-        pDst->minSpl = src.min_spl;
-        pDst->directionality = halToDirectionality(src.directionality);
-        pDst->frequencyResponse.resize(src.num_frequency_responses);
-        for (size_t k = 0; k < src.num_frequency_responses; k++) {
-            pDst->frequencyResponse[k].frequency = src.frequency_responses[0][k];
-            pDst->frequencyResponse[k].level = src.frequency_responses[1][k];
-        }
-        pDst->position.x = src.geometric_location.x;
-        pDst->position.y = src.geometric_location.y;
-        pDst->position.z = src.geometric_location.z;
-
-        pDst->orientation.x = src.orientation.x;
-        pDst->orientation.y = src.orientation.y;
-        pDst->orientation.z = src.orientation.z;
-
-        status = true;
-    }
-    return status;
-}
-#endif
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
diff --git a/audio/core/all-versions/default/Device.cpp b/audio/core/all-versions/default/Device.cpp
index 6260ba1..130dfba 100644
--- a/audio/core/all-versions/default/Device.cpp
+++ b/audio/core/all-versions/default/Device.cpp
@@ -17,9 +17,7 @@
 #define LOG_TAG "DeviceHAL"
 
 #include "core/default/Device.h"
-#include <HidlUtils.h>
 #include "common/all-versions/default/EffectMap.h"
-#include "core/default/Conversions.h"
 #include "core/default/StreamIn.h"
 #include "core/default/StreamOut.h"
 #include "core/default/Util.h"
@@ -33,6 +31,8 @@
 
 #include <android/log.h>
 
+#include <HidlUtils.h>
+
 namespace android {
 namespace hardware {
 namespace audio {
@@ -135,13 +135,14 @@
 
 Return<void> Device::getInputBufferSize(const AudioConfig& config, getInputBufferSize_cb _hidl_cb) {
     audio_config_t halConfig;
-    HidlUtils::audioConfigToHal(config, &halConfig);
-    size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
     Result retval(Result::INVALID_ARGUMENTS);
     uint64_t bufferSize = 0;
-    if (halBufferSize != 0) {
-        retval = Result::OK;
-        bufferSize = halBufferSize;
+    if (HidlUtils::audioConfigToHal(config, &halConfig) == NO_ERROR) {
+        size_t halBufferSize = mDevice->get_input_buffer_size(mDevice, &halConfig);
+        if (halBufferSize != 0) {
+            retval = Result::OK;
+            bufferSize = halBufferSize;
+        }
     }
     _hidl_cb(retval, bufferSize);
     return Void();
@@ -150,63 +151,80 @@
 std::tuple<Result, sp<IStreamOut>> Device::openOutputStreamImpl(int32_t ioHandle,
                                                                 const DeviceAddress& device,
                                                                 const AudioConfig& config,
-                                                                AudioOutputFlagBitfield flags,
+                                                                const AudioOutputFlags& flags,
                                                                 AudioConfig* suggestedConfig) {
     audio_config_t halConfig;
-    HidlUtils::audioConfigToHal(config, &halConfig);
+    if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
     audio_stream_out_t* halStream;
-    ALOGV(
-        "open_output_stream handle: %d devices: %x flags: %#x "
-        "srate: %d format %#x channels %x address %s",
-        ioHandle, static_cast<audio_devices_t>(device.device),
-        static_cast<audio_output_flags_t>(flags), halConfig.sample_rate, halConfig.format,
-        halConfig.channel_mask, deviceAddressToHal(device).c_str());
-    int status =
-        mDevice->open_output_stream(mDevice, ioHandle, static_cast<audio_devices_t>(device.device),
-                                    static_cast<audio_output_flags_t>(flags), &halConfig,
-                                    &halStream, deviceAddressToHal(device).c_str());
+    audio_devices_t halDevice;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
+    audio_output_flags_t halFlags;
+    if (CoreUtils::audioOutputFlagsToHal(flags, &halFlags) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
+    ALOGV("open_output_stream handle: %d devices: %x flags: %#x "
+          "srate: %d format %#x channels %x address %s",
+          ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
+          halConfig.channel_mask, halDeviceAddress);
+    int status = mDevice->open_output_stream(mDevice, ioHandle, halDevice, halFlags, &halConfig,
+                                             &halStream, halDeviceAddress);
     ALOGV("open_output_stream status %d stream %p", status, halStream);
     sp<IStreamOut> streamOut;
     if (status == OK) {
         streamOut = new StreamOut(this, halStream);
         ++mOpenedStreamsCount;
     }
-    status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    status_t convertStatus =
+            HidlUtils::audioConfigFromHal(halConfig, false /*isInput*/, suggestedConfig);
     ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
     return {analyzeStatus("open_output_stream", status, {EINVAL} /*ignore*/), streamOut};
 }
 
 std::tuple<Result, sp<IStreamIn>> Device::openInputStreamImpl(
-    int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
-    AudioInputFlagBitfield flags, AudioSource source, AudioConfig* suggestedConfig) {
+        int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
+        const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig) {
     audio_config_t halConfig;
-    HidlUtils::audioConfigToHal(config, &halConfig);
+    if (HidlUtils::audioConfigToHal(config, &halConfig) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
     audio_stream_in_t* halStream;
-    ALOGV(
-        "open_input_stream handle: %d devices: %x flags: %#x "
-        "srate: %d format %#x channels %x address %s source %d",
-        ioHandle, static_cast<audio_devices_t>(device.device),
-        static_cast<audio_input_flags_t>(flags), halConfig.sample_rate, halConfig.format,
-        halConfig.channel_mask, deviceAddressToHal(device).c_str(),
-        static_cast<audio_source_t>(source));
-    int status = mDevice->open_input_stream(
-        mDevice, ioHandle, static_cast<audio_devices_t>(device.device), &halConfig, &halStream,
-        static_cast<audio_input_flags_t>(flags), deviceAddressToHal(device).c_str(),
-        static_cast<audio_source_t>(source));
+    audio_devices_t halDevice;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (CoreUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
+    audio_input_flags_t halFlags;
+    audio_source_t halSource;
+    if (CoreUtils::audioInputFlagsToHal(flags, &halFlags) != NO_ERROR ||
+        HidlUtils::audioSourceToHal(source, &halSource) != NO_ERROR) {
+        return {Result::INVALID_ARGUMENTS, nullptr};
+    }
+    ALOGV("open_input_stream handle: %d devices: %x flags: %#x "
+          "srate: %d format %#x channels %x address %s source %d",
+          ioHandle, halDevice, halFlags, halConfig.sample_rate, halConfig.format,
+          halConfig.channel_mask, halDeviceAddress, halSource);
+    int status = mDevice->open_input_stream(mDevice, ioHandle, halDevice, &halConfig, &halStream,
+                                            halFlags, halDeviceAddress, halSource);
     ALOGV("open_input_stream status %d stream %p", status, halStream);
     sp<IStreamIn> streamIn;
     if (status == OK) {
         streamIn = new StreamIn(this, halStream);
         ++mOpenedStreamsCount;
     }
-    status_t convertStatus = HidlUtils::audioConfigFromHal(halConfig, suggestedConfig);
+    status_t convertStatus =
+            HidlUtils::audioConfigFromHal(halConfig, true /*isInput*/, suggestedConfig);
     ALOGW_IF(convertStatus != OK, "%s: suggested config with incompatible fields", __func__);
     return {analyzeStatus("open_input_stream", status, {EINVAL} /*ignore*/), streamIn};
 }
 
 #if MAJOR_VERSION == 2
 Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                      const AudioConfig& config, AudioOutputFlagBitfield flags,
+                                      const AudioConfig& config, AudioOutputFlags flags,
                                       openOutputStream_cb _hidl_cb) {
     AudioConfig suggestedConfig;
     auto [result, streamOut] =
@@ -216,7 +234,7 @@
 }
 
 Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                     const AudioConfig& config, AudioInputFlagBitfield flags,
+                                     const AudioConfig& config, AudioInputFlags flags,
                                      AudioSource source, openInputStream_cb _hidl_cb) {
     AudioConfig suggestedConfig;
     auto [result, streamIn] =
@@ -227,9 +245,25 @@
 
 #elif MAJOR_VERSION >= 4
 Return<void> Device::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                      const AudioConfig& config, AudioOutputFlagBitfield flags,
+                                      const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                      AudioOutputFlags flags,
+#else
+                                      const AudioOutputFlags& flags,
+#endif
                                       const SourceMetadata& sourceMetadata,
                                       openOutputStream_cb _hidl_cb) {
+#if MAJOR_VERSION <= 6
+    if (status_t status = CoreUtils::sourceMetadataToHal(sourceMetadata, nullptr);
+        status != NO_ERROR) {
+#else
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(sourceMetadata,
+                                                           false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
+#endif
+        _hidl_cb(analyzeStatus("sourceMetadataToHal", status), nullptr, AudioConfig{});
+        return Void();
+    }
     AudioConfig suggestedConfig;
     auto [result, streamOut] =
         openOutputStreamImpl(ioHandle, device, config, flags, &suggestedConfig);
@@ -241,14 +275,29 @@
 }
 
 Return<void> Device::openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                     const AudioConfig& config, AudioInputFlagBitfield flags,
+                                     const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                     AudioInputFlags flags,
+#else
+                                     const AudioInputFlags& flags,
+#endif
                                      const SinkMetadata& sinkMetadata,
                                      openInputStream_cb _hidl_cb) {
     if (sinkMetadata.tracks.size() == 0) {
         // This should never happen, the framework must not create as stream
         // if there is no client
         ALOGE("openInputStream called without tracks connected");
-        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig());
+        _hidl_cb(Result::INVALID_ARGUMENTS, nullptr, AudioConfig{});
+        return Void();
+    }
+#if MAJOR_VERSION <= 6
+    if (status_t status = CoreUtils::sinkMetadataToHal(sinkMetadata, nullptr); status != NO_ERROR) {
+#else
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, nullptr);
+        status != NO_ERROR) {
+#endif
+        _hidl_cb(analyzeStatus("sinkMetadataToHal", status), nullptr, AudioConfig{});
         return Void();
     }
     // Pick the first one as the main.
@@ -271,9 +320,7 @@
 Return<void> Device::createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
                                       const hidl_vec<AudioPortConfig>& sinks,
                                       createAudioPatch_cb _hidl_cb) {
-    auto [retval, patch] = createOrUpdateAudioPatch(
-            static_cast<AudioPatchHandle>(AudioHandleConsts::AUDIO_PATCH_HANDLE_NONE), sources,
-            sinks);
+    auto [retval, patch] = createOrUpdateAudioPatch(AudioPatchHandle{}, sources, sinks);
     _hidl_cb(retval, patch);
     return Void();
 }
@@ -283,9 +330,17 @@
         const hidl_vec<AudioPortConfig>& sinks) {
     Result retval(Result::NOT_SUPPORTED);
     if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
-        std::unique_ptr<audio_port_config[]> halSources(HidlUtils::audioPortConfigsToHal(sources));
-        std::unique_ptr<audio_port_config[]> halSinks(HidlUtils::audioPortConfigsToHal(sinks));
         audio_patch_handle_t halPatch = static_cast<audio_patch_handle_t>(patch);
+        std::unique_ptr<audio_port_config[]> halSources;
+        if (status_t status = HidlUtils::audioPortConfigsToHal(sources, &halSources);
+            status != NO_ERROR) {
+            return {analyzeStatus("audioPortConfigsToHal;sources", status), patch};
+        }
+        std::unique_ptr<audio_port_config[]> halSinks;
+        if (status_t status = HidlUtils::audioPortConfigsToHal(sinks, &halSinks);
+            status != NO_ERROR) {
+            return {analyzeStatus("audioPortConfigsToHal;sinks", status), patch};
+        }
         retval = analyzeStatus("create_audio_patch",
                                mDevice->create_audio_patch(mDevice, sources.size(), &halSources[0],
                                                            sinks.size(), &halSinks[0], &halPatch));
@@ -305,22 +360,50 @@
     return Result::NOT_SUPPORTED;
 }
 
-Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
-    audio_port halPort;
-    HidlUtils::audioPortToHal(port, &halPort);
-    Result retval = analyzeStatus("get_audio_port", mDevice->get_audio_port(mDevice, &halPort));
+template <typename HalPort>
+Return<void> Device::getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
+                                      int (*halGetter)(audio_hw_device_t*, HalPort*),
+                                      const char* halGetterName) {
+    HalPort halPort;
+    if (status_t status = HidlUtils::audioPortToHal(port, &halPort); status != NO_ERROR) {
+        _hidl_cb(analyzeStatus("audioPortToHal", status), port);
+        return Void();
+    }
+    Result retval = analyzeStatus(halGetterName, halGetter(mDevice, &halPort));
     AudioPort resultPort = port;
     if (retval == Result::OK) {
-        HidlUtils::audioPortFromHal(halPort, &resultPort);
+        if (status_t status = HidlUtils::audioPortFromHal(halPort, &resultPort);
+            status != NO_ERROR) {
+            _hidl_cb(analyzeStatus("audioPortFromHal", status), port);
+            return Void();
+        }
     }
     _hidl_cb(retval, resultPort);
     return Void();
 }
 
+#if MAJOR_VERSION <= 6
+Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+    return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
+}
+#else
+Return<void> Device::getAudioPort(const AudioPort& port, getAudioPort_cb _hidl_cb) {
+    if (version() >= AUDIO_DEVICE_API_VERSION_3_2) {
+        // get_audio_port_v7 is mandatory if legacy HAL support this API version.
+        return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port_v7, "get_audio_port_v7");
+    } else {
+        return getAudioPortImpl(port, _hidl_cb, mDevice->get_audio_port, "get_audio_port");
+    }
+}
+#endif
+
 Return<Result> Device::setAudioPortConfig(const AudioPortConfig& config) {
     if (version() >= AUDIO_DEVICE_API_VERSION_3_0) {
         struct audio_port_config halPortConfig;
-        HidlUtils::audioPortConfigToHal(config, &halPortConfig);
+        if (status_t status = HidlUtils::audioPortConfigToHal(config, &halPortConfig);
+            status != NO_ERROR) {
+            return analyzeStatus("audioPortConfigToHal", status);
+        }
         return analyzeStatus("set_audio_port_config",
                              mDevice->set_audio_port_config(mDevice, &halPortConfig));
     }
@@ -391,7 +474,7 @@
         mDevice->get_microphones(mDevice, &mic_array[0], &actual_mics) == 0) {
         microphones.resize(actual_mics);
         for (size_t i = 0; i < actual_mics; ++i) {
-            halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
+            (void)CoreUtils::microphoneInfoFromHal(mic_array[i], &microphones[i]);
         }
         retval = Result::OK;
     }
@@ -452,7 +535,7 @@
                                       const hidl_vec<AudioPortConfig>& sources,
                                       const hidl_vec<AudioPortConfig>& sinks,
                                       createAudioPatch_cb _hidl_cb) {
-    if (previousPatch != static_cast<int32_t>(AudioHandleConsts::AUDIO_PATCH_HANDLE_NONE)) {
+    if (previousPatch != static_cast<int32_t>(AudioPatchHandle{})) {
         auto [retval, patch] = createOrUpdateAudioPatch(previousPatch, sources, sinks);
         _hidl_cb(retval, patch);
     } else {
diff --git a/audio/core/all-versions/default/ParametersUtil.cpp b/audio/core/all-versions/default/ParametersUtil.cpp
index 0c8e28a..4d53645 100644
--- a/audio/core/all-versions/default/ParametersUtil.cpp
+++ b/audio/core/all-versions/default/ParametersUtil.cpp
@@ -15,11 +15,12 @@
  */
 
 #include "core/default/ParametersUtil.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 
 #include <system/audio.h>
 
+#include <util/CoreUtils.h>
+
 namespace android {
 namespace hardware {
 namespace audio {
@@ -149,9 +150,15 @@
     }
     return setParams(params);
 }
+
 Result ParametersUtil::setParam(const char* name, const DeviceAddress& address) {
-    AudioParameter params(String8(deviceAddressToHal(address).c_str()));
-    params.addInt(String8(name), int(address.device));
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (CoreUtils::deviceAddressToHal(address, &halDeviceType, halDeviceAddress) != NO_ERROR) {
+        return Result::INVALID_ARGUMENTS;
+    }
+    AudioParameter params{String8(halDeviceAddress)};
+    params.addInt(String8(name), halDeviceType);
     return setParams(params);
 }
 
diff --git a/audio/core/all-versions/default/PrimaryDevice.cpp b/audio/core/all-versions/default/PrimaryDevice.cpp
index 11c1c5a..fe56177 100644
--- a/audio/core/all-versions/default/PrimaryDevice.cpp
+++ b/audio/core/all-versions/default/PrimaryDevice.cpp
@@ -73,28 +73,36 @@
 
 #if MAJOR_VERSION == 2
 Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                             const AudioConfig& config,
-                                             AudioOutputFlagBitfield flags,
+                                             const AudioConfig& config, AudioOutputFlags flags,
                                              openOutputStream_cb _hidl_cb) {
     return mDevice->openOutputStream(ioHandle, device, config, flags, _hidl_cb);
 }
 
 Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                            const AudioConfig& config, AudioInputFlagBitfield flags,
+                                            const AudioConfig& config, AudioInputFlags flags,
                                             AudioSource source, openInputStream_cb _hidl_cb) {
     return mDevice->openInputStream(ioHandle, device, config, flags, source, _hidl_cb);
 }
 #elif MAJOR_VERSION >= 4
 Return<void> PrimaryDevice::openOutputStream(int32_t ioHandle, const DeviceAddress& device,
                                              const AudioConfig& config,
-                                             AudioOutputFlagBitfield flags,
+#if MAJOR_VERSION <= 6
+                                             AudioOutputFlags flags,
+#else
+                                             const AudioOutputFlags& flags,
+#endif
                                              const SourceMetadata& sourceMetadata,
                                              openOutputStream_cb _hidl_cb) {
     return mDevice->openOutputStream(ioHandle, device, config, flags, sourceMetadata, _hidl_cb);
 }
 
 Return<void> PrimaryDevice::openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                            const AudioConfig& config, AudioInputFlagBitfield flags,
+                                            const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                            AudioInputFlags flags,
+#else
+                                            const AudioInputFlags& flags,
+#endif
                                             const SinkMetadata& sinkMetadata,
                                             openInputStream_cb _hidl_cb) {
     return mDevice->openInputStream(ioHandle, device, config, flags, sinkMetadata, _hidl_cb);
diff --git a/audio/core/all-versions/default/Stream.cpp b/audio/core/all-versions/default/Stream.cpp
index 74e5945..7e32573 100644
--- a/audio/core/all-versions/default/Stream.cpp
+++ b/audio/core/all-versions/default/Stream.cpp
@@ -17,17 +17,19 @@
 #define LOG_TAG "StreamHAL"
 
 #include "core/default/Stream.h"
+#include "common/all-versions/HidlSupport.h"
 #include "common/all-versions/default/EffectMap.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 
 #include <inttypes.h>
 
+#include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
 #include <hardware/audio_effect.h>
 #include <media/AudioContainers.h>
 #include <media/TypeConverter.h>
+#include <util/CoreUtils.h>
 
 namespace android {
 namespace hardware {
@@ -35,7 +37,12 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-Stream::Stream(audio_stream_t* stream) : mStream(stream) {}
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::utils::splitString;
+
+Stream::Stream(bool isInput, audio_stream_t* stream) : mIsInput(isInput), mStream(stream) {
+    (void)mIsInput;  // prevent 'unused field' warnings in pre-V7 versions.
+}
 
 Stream::~Stream() {
     mStream = nullptr;
@@ -78,6 +85,7 @@
     return mStream->get_buffer_size(mStream);
 }
 
+#if MAJOR_VERSION <= 6
 Return<uint32_t> Stream::getSampleRate() {
     return mStream->get_sample_rate(mStream);
 }
@@ -201,6 +209,109 @@
     return Void();
 }
 
+#else  // MAJOR_VERSION <= 6
+
+Return<void> Stream::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+    String8 halListValue;
+    Result result = getParam(AudioParameter::keyStreamSupportedFormats, &halListValue);
+    hidl_vec<AudioProfile> profiles;
+    if (result != Result::OK) {
+        _hidl_cb(result, profiles);
+        return Void();
+    }
+    // Ensure that the separator is one character, despite that it's defined as a C string.
+    static_assert(sizeof(AUDIO_PARAMETER_VALUE_LIST_SEPARATOR) == 2);
+    std::vector<std::string> halFormats =
+            splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+    hidl_vec<AudioFormat> formats;
+    (void)HidlUtils::audioFormatsFromHal(halFormats, &formats);
+    std::vector<AudioProfile> tempProfiles;
+    for (const auto& format : formats) {
+        audio_format_t halFormat;
+        if (status_t status = HidlUtils::audioFormatToHal(format, &halFormat); status != NO_ERROR) {
+            continue;
+        }
+        AudioParameter context;
+        context.addInt(String8(AUDIO_PARAMETER_STREAM_FORMAT), int(halFormat));
+        // Query supported sample rates for the format.
+        result = getParam(AudioParameter::keyStreamSupportedSamplingRates, &halListValue, context);
+        if (result != Result::OK) break;
+        std::vector<std::string> halSampleRates =
+                splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+        hidl_vec<uint32_t> sampleRates;
+        sampleRates.resize(halSampleRates.size());
+        for (size_t i = 0; i < sampleRates.size(); ++i) {
+            sampleRates[i] = std::stoi(halSampleRates[i]);
+        }
+        // Query supported channel masks for the format.
+        result = getParam(AudioParameter::keyStreamSupportedChannels, &halListValue, context);
+        if (result != Result::OK) break;
+        std::vector<std::string> halChannelMasks =
+                splitString(halListValue.string(), AUDIO_PARAMETER_VALUE_LIST_SEPARATOR[0]);
+        hidl_vec<AudioChannelMask> channelMasks;
+        (void)HidlUtils::audioChannelMasksFromHal(halChannelMasks, &channelMasks);
+        // Create a profile.
+        if (channelMasks.size() != 0 && sampleRates.size() != 0) {
+            tempProfiles.push_back({.format = format,
+                                    .sampleRates = std::move(sampleRates),
+                                    .channelMasks = std::move(channelMasks)});
+        }
+    }
+    // Legacy get_parameter does not return a status_t, thus can not advertise of failure.
+    // Note that the method must not return an empty list if this capability is supported.
+    if (!tempProfiles.empty()) {
+        profiles = tempProfiles;
+    } else {
+        result = Result::NOT_SUPPORTED;
+    }
+    _hidl_cb(result, profiles);
+    return Void();
+}
+
+Return<void> Stream::getAudioProperties(getAudioProperties_cb _hidl_cb) {
+    audio_config_base_t halConfigBase = {mStream->get_sample_rate(mStream),
+                                         mStream->get_channels(mStream),
+                                         mStream->get_format(mStream)};
+    AudioConfigBase configBase = {};
+    status_t status = HidlUtils::audioConfigBaseFromHal(halConfigBase, mIsInput, &configBase);
+    _hidl_cb(Stream::analyzeStatus("get_audio_properties", status), configBase);
+    return Void();
+}
+
+Return<Result> Stream::setAudioProperties(const AudioConfigBaseOptional& config) {
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    bool formatSpecified, sRateSpecified, channelMaskSpecified;
+    status_t status = HidlUtils::audioConfigBaseOptionalToHal(
+            config, &halConfigBase, &formatSpecified, &sRateSpecified, &channelMaskSpecified);
+    if (status != NO_ERROR) {
+        return Stream::analyzeStatus("set_audio_properties", status);
+    }
+    if (sRateSpecified) {
+        if (Result result = setParam(AudioParameter::keySamplingRate,
+                                     static_cast<int>(halConfigBase.sample_rate));
+            result != Result::OK) {
+            return result;
+        }
+    }
+    if (channelMaskSpecified) {
+        if (Result result = setParam(AudioParameter::keyChannels,
+                                     static_cast<int>(halConfigBase.channel_mask));
+            result != Result::OK) {
+            return result;
+        }
+    }
+    if (formatSpecified) {
+        if (Result result =
+                    setParam(AudioParameter::keyFormat, static_cast<int>(halConfigBase.format));
+            result != Result::OK) {
+            return result;
+        }
+    }
+    return Result::OK;
+}
+
+#endif  // MAJOR_VERSION <= 6
+
 Return<Result> Stream::addEffect(uint64_t effectId) {
     effect_handle_t halEffect = EffectMap::getInstance().get(effectId);
     if (halEffect != NULL) {
@@ -257,12 +368,15 @@
 }
 #elif MAJOR_VERSION >= 4
 Return<void> Stream::getDevices(getDevices_cb _hidl_cb) {
-    int device = 0;
-    Result retval = getParam(AudioParameter::keyRouting, &device);
+    int halDevice = 0;
+    Result retval = getParam(AudioParameter::keyRouting, &halDevice);
     hidl_vec<DeviceAddress> devices;
     if (retval == Result::OK) {
         devices.resize(1);
-        devices[0].device = static_cast<AudioDevice>(device);
+        retval = Stream::analyzeStatus(
+                "get_devices",
+                CoreUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halDevice), nullptr,
+                                                &devices[0]));
     }
     _hidl_cb(retval, devices);
     return Void();
@@ -273,14 +387,13 @@
     if (devices.size() > 1) {
         return Result::NOT_SUPPORTED;
     }
-    DeviceAddress address;
+    DeviceAddress address{};
     if (devices.size() == 1) {
         address = devices[0];
-    } else {
-        address.device = AudioDevice::NONE;
     }
     return setParam(AudioParameter::keyRouting, address);
 }
+
 Return<void> Stream::getParameters(const hidl_vec<ParameterValue>& context,
                                    const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
     getParametersImpl(context, keys, _hidl_cb);
diff --git a/audio/core/all-versions/default/StreamIn.cpp b/audio/core/all-versions/default/StreamIn.cpp
index d38e225..17621a9 100644
--- a/audio/core/all-versions/default/StreamIn.cpp
+++ b/audio/core/all-versions/default/StreamIn.cpp
@@ -17,18 +17,19 @@
 #define LOG_TAG "StreamInHAL"
 
 #include "core/default/StreamIn.h"
-#include "core/default/Conversions.h"
 #include "core/default/Util.h"
 #include "common/all-versions/HidlSupport.h"
 
 //#define LOG_NDEBUG 0
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
+#include <HidlUtils.h>
 #include <android/log.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
-#include <memory>
 #include <cmath>
+#include <memory>
 
 namespace android {
 namespace hardware {
@@ -36,6 +37,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
 namespace {
 
 class ReadThread : public Thread {
@@ -141,7 +144,7 @@
 StreamIn::StreamIn(const sp<Device>& device, audio_stream_in_t* stream)
     : mDevice(device),
       mStream(stream),
-      mStreamCommon(new Stream(&stream->common)),
+      mStreamCommon(new Stream(true /*isInput*/, &stream->common)),
       mStreamMmap(new StreamMmap<audio_stream_in_t>(stream)),
       mEfGroup(nullptr),
       mStopReadThread(false) {}
@@ -177,6 +180,7 @@
     return mStreamCommon->getBufferSize();
 }
 
+#if MAJOR_VERSION <= 6
 Return<uint32_t> StreamIn::getSampleRate() {
     return mStreamCommon->getSampleRate();
 }
@@ -223,6 +227,18 @@
     return mStreamCommon->setFormat(format);
 }
 
+#else
+
+Return<void> StreamIn::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+    return mStreamCommon->getSupportedProfiles(_hidl_cb);
+}
+
+Return<Result> StreamIn::setAudioProperties(const AudioConfigBaseOptional& config) {
+    return mStreamCommon->setAudioProperties(config);
+}
+
+#endif  // MAJOR_VERSION <= 6
+
 Return<void> StreamIn::getAudioProperties(getAudioProperties_cb _hidl_cb) {
     return mStreamCommon->getAudioProperties(_hidl_cb);
 }
@@ -321,9 +337,11 @@
 Return<void> StreamIn::getAudioSource(getAudioSource_cb _hidl_cb) {
     int halSource;
     Result retval = mStreamCommon->getParam(AudioParameter::keyInputSource, &halSource);
-    AudioSource source(AudioSource::DEFAULT);
+    AudioSource source = {};
     if (retval == Result::OK) {
-        source = AudioSource(halSource);
+        retval = Stream::analyzeStatus(
+                "get_audio_source",
+                HidlUtils::audioSourceFromHal(static_cast<audio_source_t>(halSource), &source));
     }
     _hidl_cb(retval, source);
     return Void();
@@ -340,7 +358,11 @@
 Return<void> StreamIn::prepareForReading(uint32_t frameSize, uint32_t framesCount,
                                          prepareForReading_cb _hidl_cb) {
     status_t status;
+#if MAJOR_VERSION <= 6
     ThreadInfo threadInfo = {0, 0};
+#else
+    int32_t threadInfo = 0;
+#endif
 
     // Wrap the _hidl_cb to return an error
     auto sendError = [&threadInfo, &_hidl_cb](Result result) {
@@ -410,8 +432,12 @@
     mStatusMQ = std::move(tempStatusMQ);
     mReadThread = tempReadThread;
     mEfGroup = tempElfGroup.release();
+#if MAJOR_VERSION <= 6
     threadInfo.pid = getpid();
     threadInfo.tid = mReadThread->getTid();
+#else
+    threadInfo = mReadThread->getTid();
+#endif
     _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
              threadInfo);
     return Void();
@@ -452,34 +478,74 @@
 }
 
 #if MAJOR_VERSION >= 4
-Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
-    if (mStream->update_sink_metadata == nullptr) {
-        return Void();  // not supported by the HAL
-    }
+Result StreamIn::doUpdateSinkMetadata(const SinkMetadata& sinkMetadata) {
     std::vector<record_track_metadata> halTracks;
-    halTracks.reserve(sinkMetadata.tracks.size());
-    for (auto& metadata : sinkMetadata.tracks) {
-        record_track_metadata halTrackMetadata = {
-            .source = static_cast<audio_source_t>(metadata.source), .gain = metadata.gain};
-#if MAJOR_VERSION >= 5
-        if (metadata.destination.getDiscriminator() ==
-            RecordTrackMetadata::Destination::hidl_discriminator::device) {
-            halTrackMetadata.dest_device =
-                static_cast<audio_devices_t>(metadata.destination.device().device);
-            strncpy(halTrackMetadata.dest_device_address,
-                    deviceAddressToHal(metadata.destination.device()).c_str(),
-                    AUDIO_DEVICE_MAX_ADDRESS_LEN);
+#if MAJOR_VERSION <= 6
+    (void)CoreUtils::sinkMetadataToHal(sinkMetadata, &halTracks);
+#else
+    // Validate whether a conversion to V7 is possible. This is needed
+    // to have a consistent behavior of the HAL regardless of the API
+    // version of the legacy HAL (and also to be consistent with openInputStream).
+    std::vector<record_track_metadata_v7> halTracksV7;
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(
+                sinkMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
+        status == NO_ERROR) {
+        halTracks.reserve(halTracksV7.size());
+        for (auto metadata_v7 : halTracksV7) {
+            halTracks.push_back(std::move(metadata_v7.base));
         }
-#endif
-        halTracks.push_back(halTrackMetadata);
+    } else {
+        return Stream::analyzeStatus("sinkMetadataToHal", status);
     }
+#endif  // MAJOR_VERSION <= 6
     const sink_metadata_t halMetadata = {
         .track_count = halTracks.size(),
         .tracks = halTracks.data(),
     };
     mStream->update_sink_metadata(mStream, &halMetadata);
+    return Result::OK;
+}
+
+#if MAJOR_VERSION >= 7
+Result StreamIn::doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata) {
+    std::vector<record_track_metadata_v7> halTracks;
+    if (status_t status = CoreUtils::sinkMetadataToHalV7(sinkMetadata,
+                                                         false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
+        return Stream::analyzeStatus("sinkMetadataToHal", status);
+    }
+    const sink_metadata_v7_t halMetadata = {
+            .track_count = halTracks.size(),
+            .tracks = halTracks.data(),
+    };
+    mStream->update_sink_metadata_v7(mStream, &halMetadata);
+    return Result::OK;
+}
+#endif  //  MAJOR_VERSION >= 7
+
+#if MAJOR_VERSION <= 6
+Return<void> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+    if (mStream->update_sink_metadata == nullptr) {
+        return Void();  // not supported by the HAL
+    }
+    (void)doUpdateSinkMetadata(sinkMetadata);
     return Void();
 }
+#elif MAJOR_VERSION >= 7
+Return<Result> StreamIn::updateSinkMetadata(const SinkMetadata& sinkMetadata) {
+    if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
+        if (mStream->update_sink_metadata == nullptr) {
+            return Result::NOT_SUPPORTED;
+        }
+        return doUpdateSinkMetadata(sinkMetadata);
+    } else {
+        if (mStream->update_sink_metadata_v7 == nullptr) {
+            return Result::NOT_SUPPORTED;
+        }
+        return doUpdateSinkMetadataV7(sinkMetadata);
+    }
+}
+#endif
 
 Return<void> StreamIn::getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) {
     Result retval = Result::NOT_SUPPORTED;
@@ -491,7 +557,7 @@
         mStream->get_active_microphones(mStream, &mic_array[0], &actual_mics) == 0) {
         microphones.resize(actual_mics);
         for (size_t i = 0; i < actual_mics; ++i) {
-            halToMicrophoneCharacteristics(&microphones[i], mic_array[i]);
+            (void)CoreUtils::microphoneInfoFromHal(mic_array[i], &microphones[i]);
         }
         retval = Result::OK;
     }
diff --git a/audio/core/all-versions/default/StreamOut.cpp b/audio/core/all-versions/default/StreamOut.cpp
index af3307a..c23922d 100644
--- a/audio/core/all-versions/default/StreamOut.cpp
+++ b/audio/core/all-versions/default/StreamOut.cpp
@@ -26,8 +26,11 @@
 
 #include <memory>
 
+#include <HidlUtils.h>
 #include <android/log.h>
+#include <audio_utils/Metadata.h>
 #include <hardware/audio.h>
+#include <util/CoreUtils.h>
 #include <utils/Trace.h>
 
 namespace android {
@@ -36,6 +39,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+
 namespace {
 
 class WriteThread : public Thread {
@@ -142,7 +147,7 @@
 StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream)
     : mDevice(device),
       mStream(stream),
-      mStreamCommon(new Stream(&stream->common)),
+      mStreamCommon(new Stream(false /*isInput*/, &stream->common)),
       mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
       mEfGroup(nullptr),
       mStopWriteThread(false) {}
@@ -182,6 +187,7 @@
     return mStreamCommon->getBufferSize();
 }
 
+#if MAJOR_VERSION <= 6
 Return<uint32_t> StreamOut::getSampleRate() {
     return mStreamCommon->getSampleRate();
 }
@@ -228,6 +234,18 @@
     return mStreamCommon->setFormat(format);
 }
 
+#else
+
+Return<void> StreamOut::getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) {
+    return mStreamCommon->getSupportedProfiles(_hidl_cb);
+}
+
+Return<Result> StreamOut::setAudioProperties(const AudioConfigBaseOptional& config) {
+    return mStreamCommon->setAudioProperties(config);
+}
+
+#endif  // MAJOR_VERSION <= 6
+
 Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
     return mStreamCommon->getAudioProperties(_hidl_cb);
 }
@@ -327,7 +345,11 @@
 Return<void> StreamOut::prepareForWriting(uint32_t frameSize, uint32_t framesCount,
                                           prepareForWriting_cb _hidl_cb) {
     status_t status;
+#if MAJOR_VERSION <= 6
     ThreadInfo threadInfo = {0, 0};
+#else
+    int32_t threadInfo = 0;
+#endif
 
     // Wrap the _hidl_cb to return an error
     auto sendError = [&threadInfo, &_hidl_cb](Result result) {
@@ -396,8 +418,12 @@
     mStatusMQ = std::move(tempStatusMQ);
     mWriteThread = tempWriteThread;
     mEfGroup = tempElfGroup.release();
+#if MAJOR_VERSION <= 6
     threadInfo.pid = getpid();
     threadInfo.tid = mWriteThread->getTid();
+#else
+    threadInfo = mWriteThread->getTid();
+#endif
     _hidl_cb(Result::OK, *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(),
              threadInfo);
     return Void();
@@ -496,11 +522,11 @@
 }
 
 Return<Result> StreamOut::drain(AudioDrain type) {
+    audio_drain_type_t halDrainType =
+            type == AudioDrain::EARLY_NOTIFY ? AUDIO_DRAIN_EARLY_NOTIFY : AUDIO_DRAIN_ALL;
     return mStream->drain != NULL
-                   ? Stream::analyzeStatus(
-                             "drain",
-                             mStream->drain(mStream, static_cast<audio_drain_type_t>(type)),
-                             {ENOSYS} /*ignore*/)
+                   ? Stream::analyzeStatus("drain", mStream->drain(mStream, halDrainType),
+                                           {ENOSYS} /*ignore*/)
                    : Result::NOT_SUPPORTED;
 }
 
@@ -561,26 +587,75 @@
 }
 
 #if MAJOR_VERSION >= 4
-Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
-    if (mStream->update_source_metadata == nullptr) {
-        return Void();  // not supported by the HAL
+Result StreamOut::doUpdateSourceMetadata(const SourceMetadata& sourceMetadata) {
+    std::vector<playback_track_metadata_t> halTracks;
+#if MAJOR_VERSION <= 6
+    (void)CoreUtils::sourceMetadataToHal(sourceMetadata, &halTracks);
+#else
+    // Validate whether a conversion to V7 is possible. This is needed
+    // to have a consistent behavior of the HAL regardless of the API
+    // version of the legacy HAL (and also to be consistent with openOutputStream).
+    std::vector<playback_track_metadata_v7> halTracksV7;
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(
+                sourceMetadata, false /*ignoreNonVendorTags*/, &halTracksV7);
+        status == NO_ERROR) {
+        halTracks.reserve(halTracksV7.size());
+        for (auto metadata_v7 : halTracksV7) {
+            halTracks.push_back(std::move(metadata_v7.base));
+        }
+    } else {
+        return Stream::analyzeStatus("sourceMetadataToHal", status);
     }
-    std::vector<playback_track_metadata> halTracks;
-    halTracks.reserve(sourceMetadata.tracks.size());
-    for (auto& metadata : sourceMetadata.tracks) {
-        halTracks.push_back({
-            .usage = static_cast<audio_usage_t>(metadata.usage),
-            .content_type = static_cast<audio_content_type_t>(metadata.contentType),
-            .gain = metadata.gain,
-        });
-    }
+#endif  // MAJOR_VERSION <= 6
     const source_metadata_t halMetadata = {
         .track_count = halTracks.size(),
         .tracks = halTracks.data(),
     };
     mStream->update_source_metadata(mStream, &halMetadata);
+    return Result::OK;
+}
+
+#if MAJOR_VERSION >= 7
+Result StreamOut::doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata) {
+    std::vector<playback_track_metadata_v7> halTracks;
+    if (status_t status = CoreUtils::sourceMetadataToHalV7(
+                sourceMetadata, false /*ignoreNonVendorTags*/, &halTracks);
+        status != NO_ERROR) {
+        return Stream::analyzeStatus("sourceMetadataToHal", status);
+    }
+    const source_metadata_v7_t halMetadata = {
+            .track_count = halTracks.size(),
+            .tracks = halTracks.data(),
+    };
+    mStream->update_source_metadata_v7(mStream, &halMetadata);
+    return Result::OK;
+}
+#endif  //  MAJOR_VERSION >= 7
+
+#if MAJOR_VERSION <= 6
+Return<void> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+    if (mStream->update_source_metadata == nullptr) {
+        return Void();  // not supported by the HAL
+    }
+    (void)doUpdateSourceMetadata(sourceMetadata);
     return Void();
 }
+#elif MAJOR_VERSION >= 7
+Return<Result> StreamOut::updateSourceMetadata(const SourceMetadata& sourceMetadata) {
+    if (mDevice->version() < AUDIO_DEVICE_API_VERSION_3_2) {
+        if (mStream->update_source_metadata == nullptr) {
+            return Result::NOT_SUPPORTED;
+        }
+        return doUpdateSourceMetadata(sourceMetadata);
+    } else {
+        if (mStream->update_source_metadata_v7 == nullptr) {
+            return Result::NOT_SUPPORTED;
+        }
+        return doUpdateSourceMetadataV7(sourceMetadata);
+    }
+}
+#endif
+
 Return<Result> StreamOut::selectPresentation(int32_t /*presentationId*/, int32_t /*programId*/) {
     return Result::NOT_SUPPORTED;  // TODO: propagate to legacy
 }
@@ -588,32 +663,65 @@
 
 #if MAJOR_VERSION >= 6
 Return<void> StreamOut::getDualMonoMode(getDualMonoMode_cb _hidl_cb) {
-    _hidl_cb(Result::NOT_SUPPORTED, DualMonoMode::OFF);
+    audio_dual_mono_mode_t mode = AUDIO_DUAL_MONO_MODE_OFF;
+    Result retval = mStream->get_dual_mono_mode != nullptr
+                            ? Stream::analyzeStatus("get_dual_mono_mode",
+                                                    mStream->get_dual_mono_mode(mStream, &mode))
+                            : Result::NOT_SUPPORTED;
+    _hidl_cb(retval, DualMonoMode(mode));
     return Void();
 }
 
-Return<Result> StreamOut::setDualMonoMode(DualMonoMode /*mode*/) {
-    return Result::NOT_SUPPORTED;
+Return<Result> StreamOut::setDualMonoMode(DualMonoMode mode) {
+    return mStream->set_dual_mono_mode != nullptr
+                   ? Stream::analyzeStatus(
+                             "set_dual_mono_mode",
+                             mStream->set_dual_mono_mode(mStream,
+                                                         static_cast<audio_dual_mono_mode_t>(mode)))
+                   : Result::NOT_SUPPORTED;
 }
 
 Return<void> StreamOut::getAudioDescriptionMixLevel(getAudioDescriptionMixLevel_cb _hidl_cb) {
-    _hidl_cb(Result::NOT_SUPPORTED, -std::numeric_limits<float>::infinity());
+    float leveldB = -std::numeric_limits<float>::infinity();
+    Result retval = mStream->get_audio_description_mix_level != nullptr
+                            ? Stream::analyzeStatus(
+                                      "get_audio_description_mix_level",
+                                      mStream->get_audio_description_mix_level(mStream, &leveldB))
+                            : Result::NOT_SUPPORTED;
+    _hidl_cb(retval, leveldB);
     return Void();
 }
 
-Return<Result> StreamOut::setAudioDescriptionMixLevel(float /*leveldB*/) {
-    return Result::NOT_SUPPORTED;
+Return<Result> StreamOut::setAudioDescriptionMixLevel(float leveldB) {
+    return mStream->set_audio_description_mix_level != nullptr
+                   ? Stream::analyzeStatus(
+                             "set_audio_description_mix_level",
+                             mStream->set_audio_description_mix_level(mStream, leveldB))
+                   : Result::NOT_SUPPORTED;
 }
 
 Return<void> StreamOut::getPlaybackRateParameters(getPlaybackRateParameters_cb _hidl_cb) {
-    _hidl_cb(Result::NOT_SUPPORTED,
-             // Same as AUDIO_PLAYBACK_RATE_INITIALIZER
-             PlaybackRate{1.0f, 1.0f, TimestretchMode::DEFAULT, TimestretchFallbackMode::FAIL});
+    audio_playback_rate_t rate = AUDIO_PLAYBACK_RATE_INITIALIZER;
+    Result retval =
+            mStream->get_playback_rate_parameters != nullptr
+                    ? Stream::analyzeStatus("get_playback_rate_parameters",
+                                            mStream->get_playback_rate_parameters(mStream, &rate))
+                    : Result::NOT_SUPPORTED;
+    _hidl_cb(retval,
+             PlaybackRate{rate.mSpeed, rate.mPitch, static_cast<TimestretchMode>(rate.mStretchMode),
+                          static_cast<TimestretchFallbackMode>(rate.mFallbackMode)});
     return Void();
 }
 
-Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate& /*playbackRate*/) {
-    return Result::NOT_SUPPORTED;
+Return<Result> StreamOut::setPlaybackRateParameters(const PlaybackRate& playbackRate) {
+    audio_playback_rate_t rate = {
+            playbackRate.speed, playbackRate.pitch,
+            static_cast<audio_timestretch_stretch_mode_t>(playbackRate.timestretchMode),
+            static_cast<audio_timestretch_fallback_mode_t>(playbackRate.fallbackMode)};
+    return mStream->set_playback_rate_parameters != nullptr
+                   ? Stream::analyzeStatus("set_playback_rate_parameters",
+                                           mStream->set_playback_rate_parameters(mStream, &rate))
+                   : Result::NOT_SUPPORTED;
 }
 
 Return<Result> StreamOut::setEventCallback(const sp<IStreamOutEventCallback>& callback) {
@@ -635,7 +743,11 @@
     switch (event) {
         case STREAM_EVENT_CBK_TYPE_CODEC_FORMAT_CHANGED: {
             hidl_vec<uint8_t> audioMetadata;
-            audioMetadata.setToExternal((uint8_t*)param, strlen((char*)param));
+            // void* param is the byte string buffer from byte_string_from_audio_metadata().
+            // As the byte string buffer may have embedded zeroes, we cannot use strlen()
+            // but instead use audio_utils::metadata::dataByteStringLen().
+            audioMetadata.setToExternal((uint8_t*)param, audio_utils::metadata::dataByteStringLen(
+                                                                 (const uint8_t*)param));
             result = eventCallback->onCodecFormatChanged(audioMetadata);
         } break;
         default:
diff --git a/audio/core/all-versions/default/TEST_MAPPING b/audio/core/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..1e29440
--- /dev/null
+++ b/audio/core/all-versions/default/TEST_MAPPING
@@ -0,0 +1,13 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio@7.0-util_tests"
+    },
+    {
+      "name": "HalAudioV6_0GeneratorTest"
+    },
+    {
+      "name": "HalAudioV7_0GeneratorTest"
+    }
+  ]
+}
diff --git a/audio/core/all-versions/default/include/core/default/Conversions.h b/audio/core/all-versions/default/include/core/default/Conversions.h
deleted file mode 100644
index cb7914f..0000000
--- a/audio/core/all-versions/default/include/core/default/Conversions.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright (C) 2018 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_AUDIO_CONVERSIONS_H_
-#define ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
-
-#include PATH(android/hardware/audio/FILE_VERSION/types.h)
-
-#include <string>
-
-#include <system/audio.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace CPP_VERSION {
-namespace implementation {
-
-using namespace ::android::hardware::audio::common::CPP_VERSION;
-using namespace ::android::hardware::audio::CPP_VERSION;
-
-std::string deviceAddressToHal(const DeviceAddress& address);
-
-#if MAJOR_VERSION >= 4
-bool halToMicrophoneCharacteristics(MicrophoneInfo* pDst,
-                                    const struct audio_microphone_characteristic_t& src);
-#endif
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_AUDIO_CONVERSIONS_H_
diff --git a/audio/core/all-versions/default/include/core/default/Device.h b/audio/core/all-versions/default/include/core/default/Device.h
index b0e72d9..94cad53 100644
--- a/audio/core/all-versions/default/include/core/default/Device.h
+++ b/audio/core/all-versions/default/include/core/default/Device.h
@@ -31,6 +31,7 @@
 #include <hidl/MQDescriptor.h>
 
 #include <VersionUtils.h>
+#include <util/CoreUtils.h>
 
 namespace android {
 namespace hardware {
@@ -43,10 +44,10 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
-using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
-using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::CPP_VERSION;
+using AudioInputFlags = CoreUtils::AudioInputFlags;
+using AudioOutputFlags = CoreUtils::AudioOutputFlags;
 
 struct Device : public IDevice, public ParametersUtil {
     explicit Device(audio_hw_device_t* device);
@@ -65,28 +66,36 @@
     std::tuple<Result, sp<IStreamOut>> openOutputStreamImpl(int32_t ioHandle,
                                                             const DeviceAddress& device,
                                                             const AudioConfig& config,
-                                                            AudioOutputFlagBitfield flags,
+                                                            const AudioOutputFlags& flags,
                                                             AudioConfig* suggestedConfig);
     std::tuple<Result, sp<IStreamIn>> openInputStreamImpl(
-        int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
-        AudioInputFlagBitfield flags, AudioSource source, AudioConfig* suggestedConfig);
-#if MAJOR_VERSION == 2
+            int32_t ioHandle, const DeviceAddress& device, const AudioConfig& config,
+            const AudioInputFlags& flags, AudioSource source, AudioConfig* suggestedConfig);
+
     Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                  const AudioConfig& config, AudioOutputFlagBitfield flags,
-                                  openOutputStream_cb _hidl_cb) override;
-    Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                 const AudioConfig& config, AudioInputFlagBitfield flags,
-                                 AudioSource source, openInputStream_cb _hidl_cb) override;
-#elif MAJOR_VERSION >= 4
-    Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                  const AudioConfig& config, AudioOutputFlagBitfield flags,
-                                  const SourceMetadata& sourceMetadata,
-                                  openOutputStream_cb _hidl_cb) override;
-    Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                 const AudioConfig& config, AudioInputFlagBitfield flags,
-                                 const SinkMetadata& sinkMetadata,
-                                 openInputStream_cb _hidl_cb) override;
+                                  const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                  AudioOutputFlags flags,
+#else
+                                  const AudioOutputFlags& flags,
 #endif
+#if MAJOR_VERSION >= 4
+                                  const SourceMetadata& sourceMetadata,
+#endif
+                                  openOutputStream_cb _hidl_cb) override;
+    Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
+                                 const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                 AudioInputFlags flags,
+#else
+                                 const AudioInputFlags& flags,
+#endif
+#if MAJOR_VERSION == 2
+                                 AudioSource source,
+#elif MAJOR_VERSION >= 4
+                                 const SinkMetadata& sinkMetadata,
+#endif
+                                 openInputStream_cb _hidl_cb) override;
 
     Return<bool> supportsAudioPatches() override;
     Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
@@ -131,6 +140,8 @@
     void closeOutputStream(audio_stream_out_t* stream);
     audio_hw_device_t* device() const { return mDevice; }
 
+    uint32_t version() const { return mDevice->common.version; }
+
   private:
     bool mIsClosed;
     audio_hw_device_t* mDevice;
@@ -142,12 +153,14 @@
     std::tuple<Result, AudioPatchHandle> createOrUpdateAudioPatch(
             AudioPatchHandle patch, const hidl_vec<AudioPortConfig>& sources,
             const hidl_vec<AudioPortConfig>& sinks);
+    template <typename HalPort>
+    Return<void> getAudioPortImpl(const AudioPort& port, getAudioPort_cb _hidl_cb,
+                                  int (*halGetter)(audio_hw_device_t*, HalPort*),
+                                  const char* halGetterName);
 
     // Methods from ParametersUtil.
     char* halGetParameters(const char* keys) override;
     int halSetParameters(const char* keysAndValues) override;
-
-    uint32_t version() const { return mDevice->common.version; }
 };
 
 }  // namespace implementation
diff --git a/audio/core/all-versions/default/include/core/default/PrimaryDevice.h b/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
index ccdb7b2..5f65acf 100644
--- a/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
+++ b/audio/core/all-versions/default/include/core/default/PrimaryDevice.h
@@ -54,21 +54,29 @@
                                     getInputBufferSize_cb _hidl_cb) override;
 
     Return<void> openOutputStream(int32_t ioHandle, const DeviceAddress& device,
-                                  const AudioConfig& config, AudioOutputFlagBitfield flags,
+                                  const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                  AudioOutputFlags flags,
+#else
+                                  const AudioOutputFlags& flags,
+#endif
 #if MAJOR_VERSION >= 4
                                   const SourceMetadata& sourceMetadata,
 #endif
                                   openOutputStream_cb _hidl_cb) override;
-
     Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                 const AudioConfig& config, AudioInputFlagBitfield flags,
-                                 AudioSource source, openInputStream_cb _hidl_cb);
-#if MAJOR_VERSION >= 4
-    Return<void> openInputStream(int32_t ioHandle, const DeviceAddress& device,
-                                 const AudioConfig& config, AudioInputFlagBitfield flags,
-                                 const SinkMetadata& sinkMetadata,
-                                 openInputStream_cb _hidl_cb) override;
+                                 const AudioConfig& config,
+#if MAJOR_VERSION <= 6
+                                 AudioInputFlags flags,
+#else
+                                 const AudioInputFlags& flags,
 #endif
+#if MAJOR_VERSION == 2
+                                 AudioSource source,
+#elif MAJOR_VERSION >= 4
+                                 const SinkMetadata& sinkMetadata,
+#endif
+                                 openInputStream_cb _hidl_cb) override;
 
     Return<bool> supportsAudioPatches() override;
     Return<void> createAudioPatch(const hidl_vec<AudioPortConfig>& sources,
diff --git a/audio/core/all-versions/default/include/core/default/Stream.h b/audio/core/all-versions/default/include/core/default/Stream.h
index ce0003b..66d60e3 100644
--- a/audio/core/all-versions/default/include/core/default/Stream.h
+++ b/audio/core/all-versions/default/include/core/default/Stream.h
@@ -41,12 +41,14 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
 using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::CPP_VERSION;
 
 struct Stream : public IStream, public ParametersUtil {
-    explicit Stream(audio_stream_t* stream);
+    Stream(bool isInput, audio_stream_t* stream);
 
     /** 1GiB is the maximum buffer size the HAL client is allowed to request.
      * This value has been chosen to be under SIZE_MAX and still big enough
@@ -59,6 +61,7 @@
     Return<uint64_t> getFrameSize() override;
     Return<uint64_t> getFrameCount() override;
     Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
     Return<uint32_t> getSampleRate() override;
 #if MAJOR_VERSION == 2
     Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -72,6 +75,10 @@
     Return<AudioFormat> getFormat() override;
     Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
     Return<Result> setFormat(AudioFormat format) override;
+#else
+    Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+    Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
+#endif  // MAJOR_VERSION <= 6
     Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
     Return<Result> addEffect(uint64_t effectId) override;
     Return<Result> removeEffect(uint64_t effectId) override;
@@ -110,13 +117,14 @@
                                 const std::vector<int>& ignoreErrors);
 
    private:
-    audio_stream_t* mStream;
+     const bool mIsInput;
+     audio_stream_t* mStream;
 
-    virtual ~Stream();
+     virtual ~Stream();
 
-    // Methods from ParametersUtil.
-    char* halGetParameters(const char* keys) override;
-    int halSetParameters(const char* keysAndValues) override;
+     // Methods from ParametersUtil.
+     char* halGetParameters(const char* keys) override;
+     int halSetParameters(const char* keysAndValues) override;
 };
 
 template <typename T>
diff --git a/audio/core/all-versions/default/include/core/default/StreamIn.h b/audio/core/all-versions/default/include/core/default/StreamIn.h
index 24f9944..a980f3f 100644
--- a/audio/core/all-versions/default/include/core/default/StreamIn.h
+++ b/audio/core/all-versions/default/include/core/default/StreamIn.h
@@ -56,6 +56,7 @@
     Return<uint64_t> getFrameSize() override;
     Return<uint64_t> getFrameCount() override;
     Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
     Return<uint32_t> getSampleRate() override;
 #if MAJOR_VERSION == 2
     Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -69,6 +70,10 @@
     Return<AudioFormat> getFormat() override;
     Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
     Return<Result> setFormat(AudioFormat format) override;
+#else
+    Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+    Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
+#endif  // MAJOR_VERSION <= 6
     Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
     Return<Result> addEffect(uint64_t effectId) override;
     Return<Result> removeEffect(uint64_t effectId) override;
@@ -109,9 +114,13 @@
     Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
     Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
 #if MAJOR_VERSION >= 4
+#if MAJOR_VERSION <= 6
     Return<void> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
-    Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+#else
+    Return<Result> updateSinkMetadata(const SinkMetadata& sinkMetadata) override;
 #endif
+    Return<void> getActiveMicrophones(getActiveMicrophones_cb _hidl_cb) override;
+#endif  // MAJOR_VERSION >= 4
 #if MAJOR_VERSION >= 5
     Return<Result> setMicrophoneDirection(MicrophoneDirection direction) override;
     Return<Result> setMicrophoneFieldDimension(float zoom) override;
@@ -119,7 +128,14 @@
     static Result getCapturePositionImpl(audio_stream_in_t* stream, uint64_t* frames,
                                          uint64_t* time);
 
-   private:
+  private:
+#if MAJOR_VERSION >= 4
+    Result doUpdateSinkMetadata(const SinkMetadata& sinkMetadata);
+#if MAJOR_VERSION >= 7
+    Result doUpdateSinkMetadataV7(const SinkMetadata& sinkMetadata);
+#endif
+#endif  // MAJOR_VERSION >= 4
+
     const sp<Device> mDevice;
     audio_stream_in_t* mStream;
     const sp<Stream> mStreamCommon;
diff --git a/audio/core/all-versions/default/include/core/default/StreamOut.h b/audio/core/all-versions/default/include/core/default/StreamOut.h
index e647da9..ccc1c1a 100644
--- a/audio/core/all-versions/default/include/core/default/StreamOut.h
+++ b/audio/core/all-versions/default/include/core/default/StreamOut.h
@@ -56,6 +56,7 @@
     Return<uint64_t> getFrameSize() override;
     Return<uint64_t> getFrameCount() override;
     Return<uint64_t> getBufferSize() override;
+#if MAJOR_VERSION <= 6
     Return<uint32_t> getSampleRate() override;
 #if MAJOR_VERSION == 2
     Return<void> getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) override;
@@ -69,6 +70,10 @@
     Return<AudioFormat> getFormat() override;
     Return<void> getSupportedFormats(getSupportedFormats_cb _hidl_cb) override;
     Return<Result> setFormat(AudioFormat format) override;
+#else
+    Return<void> getSupportedProfiles(getSupportedProfiles_cb _hidl_cb) override;
+    Return<Result> setAudioProperties(const AudioConfigBaseOptional& config) override;
+#endif  // MAJOR_VERSION <= 6
     Return<void> getAudioProperties(getAudioProperties_cb _hidl_cb) override;
     Return<Result> addEffect(uint64_t effectId) override;
     Return<Result> removeEffect(uint64_t effectId) override;
@@ -118,9 +123,13 @@
     Return<void> createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) override;
     Return<void> getMmapPosition(getMmapPosition_cb _hidl_cb) override;
 #if MAJOR_VERSION >= 4
-    Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
     Return<Result> selectPresentation(int32_t presentationId, int32_t programId) override;
+#if MAJOR_VERSION <= 6
+    Return<void> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
+#else
+    Return<Result> updateSourceMetadata(const SourceMetadata& sourceMetadata) override;
 #endif
+#endif  // MAJOR_VERSION >= 4
 #if MAJOR_VERSION >= 6
     Return<void> getDualMonoMode(getDualMonoMode_cb _hidl_cb) override;
     Return<Result> setDualMonoMode(DualMonoMode mode) override;
@@ -138,6 +147,13 @@
 #endif
 
   private:
+#if MAJOR_VERSION >= 4
+    Result doUpdateSourceMetadata(const SourceMetadata& sourceMetadata);
+#if MAJOR_VERSION >= 7
+    Result doUpdateSourceMetadataV7(const SourceMetadata& sourceMetadata);
+#endif
+#endif  // MAJOR_VERSION >= 4
+
     const sp<Device> mDevice;
     audio_stream_out_t* mStream;
     const sp<Stream> mStreamCommon;
diff --git a/audio/core/all-versions/default/util/Android.bp b/audio/core/all-versions/default/util/Android.bp
new file mode 100644
index 0000000..7caf18d
--- /dev/null
+++ b/audio/core/all-versions/default/util/Android.bp
@@ -0,0 +1,147 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "android.hardware.audio-util_default",
+    defaults: ["hidl_defaults"],
+
+    vendor_available: true,
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "CoreUtils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libhidlbase",
+        "android.hardware.audio.common-util",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.audio.common-util",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+        "libhardware_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@2.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@2.0",
+        "android.hardware.audio.common@2.0-util",
+        "android.hardware.audio@2.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=2",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@4.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@4.0",
+        "android.hardware.audio.common@4.0-util",
+        "android.hardware.audio@4.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=4",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@5.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.audio.common@5.0-util",
+        "android.hardware.audio@5.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=5",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio@6.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+        "android.hardware.audio@6.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.audio@7.0-util",
+    defaults: ["android.hardware.audio-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio@7.0",
+        "libbase",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio@7.0-util_tests",
+    defaults: ["android.hardware.audio-util_default"],
+
+    srcs: ["tests/coreutils_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V7.
+    static_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio@7.0",
+        "android.hardware.audio@7.0-util",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libxml2",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/audio/core/all-versions/default/util/CoreUtils.cpp b/audio/core/all-versions/default/util/CoreUtils.cpp
new file mode 100644
index 0000000..14f76f3
--- /dev/null
+++ b/audio/core/all-versions/default/util/CoreUtils.cpp
@@ -0,0 +1,476 @@
+/*
+ * 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.
+ */
+
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#endif
+#include <HidlUtils.h>
+#include <log/log.h>
+
+#include "util/CoreUtils.h"
+
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+#if MAJOR_VERSION >= 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+#endif
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace CPP_VERSION {
+namespace implementation {
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+status_t CoreUtils::deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+                                       char* halDeviceAddress) {
+#if MAJOR_VERSION >= 5
+    return HidlUtils::deviceAddressToHal(device, halDeviceType, halDeviceAddress);
+#else
+    return HidlUtils::deviceAddressToHalImpl(device, halDeviceType, halDeviceAddress);
+#endif
+}
+
+status_t CoreUtils::deviceAddressFromHal(audio_devices_t halDeviceType,
+                                         const char* halDeviceAddress, DeviceAddress* device) {
+#if MAJOR_VERSION >= 5
+    return HidlUtils::deviceAddressFromHal(halDeviceType, halDeviceAddress, device);
+#else
+    return HidlUtils::deviceAddressFromHalImpl(halDeviceType, halDeviceAddress, device);
+#endif
+}
+
+#if MAJOR_VERSION >= 4
+status_t CoreUtils::microphoneInfoFromHal(
+        const struct audio_microphone_characteristic_t& halMicInfo, MicrophoneInfo* micInfo) {
+    status_t result = NO_ERROR;
+    micInfo->deviceId = halMicInfo.device_id;
+    CONVERT_CHECKED(
+            deviceAddressFromHal(halMicInfo.device, halMicInfo.address, &micInfo->deviceAddress),
+            result);
+    size_t chCount;
+    for (chCount = 0; chCount < AUDIO_CHANNEL_COUNT_MAX; ++chCount) {
+        if (halMicInfo.channel_mapping[chCount] == AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED) {
+            break;
+        }
+    }
+    micInfo->channelMapping.resize(chCount);
+    for (size_t ch = 0; ch < micInfo->channelMapping.size(); ch++) {
+        micInfo->channelMapping[ch] = AudioMicrophoneChannelMapping(halMicInfo.channel_mapping[ch]);
+    }
+    micInfo->location = AudioMicrophoneLocation(halMicInfo.location);
+    micInfo->group = AudioMicrophoneGroup(halMicInfo.group);
+    micInfo->indexInTheGroup = static_cast<uint32_t>(halMicInfo.index_in_the_group);
+    micInfo->sensitivity = halMicInfo.sensitivity;
+    micInfo->maxSpl = halMicInfo.max_spl;
+    micInfo->minSpl = halMicInfo.min_spl;
+    micInfo->directionality = AudioMicrophoneDirectionality(halMicInfo.directionality);
+    micInfo->frequencyResponse.resize(halMicInfo.num_frequency_responses);
+    for (size_t k = 0; k < halMicInfo.num_frequency_responses; k++) {
+        micInfo->frequencyResponse[k].frequency = halMicInfo.frequency_responses[0][k];
+        micInfo->frequencyResponse[k].level = halMicInfo.frequency_responses[1][k];
+    }
+    micInfo->position.x = halMicInfo.geometric_location.x;
+    micInfo->position.y = halMicInfo.geometric_location.y;
+    micInfo->position.z = halMicInfo.geometric_location.z;
+    micInfo->orientation.x = halMicInfo.orientation.x;
+    micInfo->orientation.y = halMicInfo.orientation.y;
+    micInfo->orientation.z = halMicInfo.orientation.z;
+    return result;
+}
+
+status_t CoreUtils::microphoneInfoToHal(const MicrophoneInfo& micInfo,
+                                        audio_microphone_characteristic_t* halMicInfo) {
+    status_t result = NO_ERROR;
+    strncpy(halMicInfo->device_id, micInfo.deviceId.c_str(), AUDIO_MICROPHONE_ID_MAX_LEN);
+    halMicInfo->device_id[AUDIO_MICROPHONE_ID_MAX_LEN - 1] = '\0';
+    if (micInfo.deviceId.size() >= AUDIO_MICROPHONE_ID_MAX_LEN) {
+        ALOGE("HIDL MicrophoneInfo device ID is too long: %zu", micInfo.deviceId.size());
+        result = BAD_VALUE;
+    }
+    CONVERT_CHECKED(
+            deviceAddressToHal(micInfo.deviceAddress, &halMicInfo->device, halMicInfo->address),
+            result);
+    if (micInfo.channelMapping.size() > AUDIO_CHANNEL_COUNT_MAX) {
+        ALOGE("HIDL MicrophoneInfo has too many channelMapping elements: %zu",
+              micInfo.channelMapping.size());
+        result = BAD_VALUE;
+    }
+    size_t ch;
+    for (ch = 0; ch < micInfo.channelMapping.size() && ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
+        halMicInfo->channel_mapping[ch] =
+                static_cast<audio_microphone_channel_mapping_t>(micInfo.channelMapping[ch]);
+    }
+    for (; ch < AUDIO_CHANNEL_COUNT_MAX; ch++) {
+        halMicInfo->channel_mapping[ch] = AUDIO_MICROPHONE_CHANNEL_MAPPING_UNUSED;
+    }
+    halMicInfo->location = static_cast<audio_microphone_location_t>(micInfo.location);
+    halMicInfo->group = static_cast<audio_microphone_group_t>(micInfo.group);
+    halMicInfo->index_in_the_group = static_cast<unsigned int>(micInfo.indexInTheGroup);
+    halMicInfo->sensitivity = micInfo.sensitivity;
+    halMicInfo->max_spl = micInfo.maxSpl;
+    halMicInfo->min_spl = micInfo.minSpl;
+    halMicInfo->directionality =
+            static_cast<audio_microphone_directionality_t>(micInfo.directionality);
+    halMicInfo->num_frequency_responses =
+            static_cast<unsigned int>(micInfo.frequencyResponse.size());
+    if (halMicInfo->num_frequency_responses > AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES) {
+        ALOGE("HIDL MicrophoneInfo has too many frequency responses: %u",
+              halMicInfo->num_frequency_responses);
+        halMicInfo->num_frequency_responses = AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES;
+        result = BAD_VALUE;
+    }
+    for (size_t k = 0; k < halMicInfo->num_frequency_responses; k++) {
+        halMicInfo->frequency_responses[0][k] = micInfo.frequencyResponse[k].frequency;
+        halMicInfo->frequency_responses[1][k] = micInfo.frequencyResponse[k].level;
+    }
+    halMicInfo->geometric_location.x = micInfo.position.x;
+    halMicInfo->geometric_location.y = micInfo.position.y;
+    halMicInfo->geometric_location.z = micInfo.position.z;
+    halMicInfo->orientation.x = micInfo.orientation.x;
+    halMicInfo->orientation.y = micInfo.orientation.y;
+    halMicInfo->orientation.z = micInfo.orientation.z;
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataFromHal(const std::vector<record_track_metadata_t>& halTracks,
+                                        SinkMetadata* sinkMetadata) {
+    status_t result = NO_ERROR;
+    sinkMetadata->tracks.resize(halTracks.size());
+    for (size_t i = 0; i < sinkMetadata->tracks.size(); ++i) {
+        const auto& halTrackMetadata = halTracks[i];
+        RecordTrackMetadata trackMetadata{};
+        CONVERT_CHECKED(
+                HidlUtils::audioSourceFromHal(halTrackMetadata.source, &trackMetadata.source),
+                result);
+        trackMetadata.gain = halTrackMetadata.gain;
+#if MAJOR_VERSION >= 5
+        if (halTrackMetadata.dest_device != AUDIO_DEVICE_NONE) {
+            DeviceAddress address;
+            if (status_t status =
+                        deviceAddressFromHal(halTrackMetadata.dest_device,
+                                             halTrackMetadata.dest_device_address, &address);
+                status == NO_ERROR) {
+                trackMetadata.destination.device(std::move(address));
+            } else {
+                result = status;
+            }
+        }
+#if MAJOR_VERSION >= 7
+        trackMetadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+#endif
+#endif  // MAJOR_VERSION >= 5
+        sinkMetadata->tracks[i] = std::move(trackMetadata);
+    }
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataFromHalV7(const std::vector<record_track_metadata_v7_t>& halTracks,
+                                          bool ignoreNonVendorTags, SinkMetadata* sinkMetadata) {
+    std::vector<record_track_metadata_t> bases;
+    bases.reserve(halTracks.size());
+    std::transform(halTracks.begin(), halTracks.end(), std::back_inserter(bases),
+                   [](const record_track_metadata_v7_t& src) -> record_track_metadata_t {
+                       record_track_metadata_t result;
+                       record_track_metadata_from_v7(&result, &src);
+                       return result;
+                   });
+    status_t result = sinkMetadataFromHal(bases, sinkMetadata);
+#if MAJOR_VERSION >= 7
+    for (size_t i = 0; i < halTracks.size(); ++i) {
+        auto& trackMetadata = sinkMetadata->tracks[i];
+        const auto& halTrackMetadata = halTracks[i];
+        CONVERT_CHECKED(
+                HidlUtils::audioChannelMaskFromHal(halTrackMetadata.channel_mask, true /*isInput*/,
+                                                   &trackMetadata.channelMask),
+                result);
+        std::vector<std::string> strTags = HidlUtils::splitAudioTags(halTrackMetadata.tags);
+        if (ignoreNonVendorTags) {
+            strTags = HidlUtils::filterOutNonVendorTags(strTags);
+        }
+        CONVERT_CHECKED(HidlUtils::audioTagsFromHal(strTags, &trackMetadata.tags), result);
+    }
+#else
+    (void)ignoreNonVendorTags;
+#endif
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+                                      std::vector<record_track_metadata_t>* halTracks) {
+    status_t result = NO_ERROR;
+    if (halTracks != nullptr) {
+        halTracks->reserve(sinkMetadata.tracks.size());
+    }
+    for (auto& trackMetadata : sinkMetadata.tracks) {
+        record_track_metadata halTrackMetadata{.gain = trackMetadata.gain};
+        CONVERT_CHECKED(HidlUtils::audioSourceToHal(trackMetadata.source, &halTrackMetadata.source),
+                        result);
+#if MAJOR_VERSION >= 5
+        if (trackMetadata.destination.getDiscriminator() ==
+            RecordTrackMetadata::Destination::hidl_discriminator::device) {
+            CONVERT_CHECKED(deviceAddressToHal(trackMetadata.destination.device(),
+                                               &halTrackMetadata.dest_device,
+                                               halTrackMetadata.dest_device_address),
+                            result);
+        }
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sinkMetadataToHalV7(const SinkMetadata& sinkMetadata, bool ignoreNonVendorTags,
+                                        std::vector<record_track_metadata_v7_t>* halTracks) {
+    std::vector<record_track_metadata> bases;
+    status_t result = sinkMetadataToHal(sinkMetadata, halTracks != nullptr ? &bases : nullptr);
+    if (halTracks != nullptr) {
+        halTracks->reserve(sinkMetadata.tracks.size());
+    }
+    for (size_t i = 0; i < sinkMetadata.tracks.size(); ++i) {
+        record_track_metadata_v7_t halTrackMetadata;
+        if (halTracks != nullptr) {
+            record_track_metadata_to_v7(&halTrackMetadata, &bases[i]);
+        }
+#if MAJOR_VERSION >= 7
+        const auto& trackMetadata = sinkMetadata.tracks[i];
+        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
+                                                         &halTrackMetadata.channel_mask),
+                        result);
+        if (ignoreNonVendorTags) {
+            CONVERT_CHECKED(
+                    HidlUtils::audioTagsToHal(HidlUtils::filterOutNonVendorTags(trackMetadata.tags),
+                                              halTrackMetadata.tags),
+                    result);
+        } else {
+            CONVERT_CHECKED(HidlUtils::audioTagsToHal(trackMetadata.tags, halTrackMetadata.tags),
+                            result);
+        }
+#else
+        (void)ignoreNonVendorTags;
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataFromHal(const std::vector<playback_track_metadata_t>& halTracks,
+                                          SourceMetadata* sourceMetadata) {
+    status_t result = NO_ERROR;
+    sourceMetadata->tracks.resize(halTracks.size());
+    for (size_t i = 0; i < sourceMetadata->tracks.size(); ++i) {
+        const auto& halTrackMetadata = halTracks[i];
+        PlaybackTrackMetadata trackMetadata{};
+        CONVERT_CHECKED(HidlUtils::audioUsageFromHal(halTrackMetadata.usage, &trackMetadata.usage),
+                        result);
+        CONVERT_CHECKED(HidlUtils::audioContentTypeFromHal(halTrackMetadata.content_type,
+                                                           &trackMetadata.contentType),
+                        result);
+        trackMetadata.gain = halTrackMetadata.gain;
+#if MAJOR_VERSION >= 7
+        trackMetadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+#endif
+        sourceMetadata->tracks[i] = std::move(trackMetadata);
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataFromHalV7(
+        const std::vector<playback_track_metadata_v7_t>& halTracks, bool ignoreNonVendorTags,
+        SourceMetadata* sourceMetadata) {
+    std::vector<playback_track_metadata_t> bases;
+    bases.reserve(halTracks.size());
+    std::transform(halTracks.begin(), halTracks.end(), std::back_inserter(bases),
+                   [](const playback_track_metadata_v7_t& src) -> playback_track_metadata_t {
+                       playback_track_metadata_t result;
+                       playback_track_metadata_from_v7(&result, &src);
+                       return result;
+                   });
+    status_t result = sourceMetadataFromHal(bases, sourceMetadata);
+#if MAJOR_VERSION >= 7
+    for (size_t i = 0; i < halTracks.size(); ++i) {
+        auto& trackMetadata = sourceMetadata->tracks[i];
+        const auto& halTrackMetadata = halTracks[i];
+        CONVERT_CHECKED(
+                HidlUtils::audioChannelMaskFromHal(halTrackMetadata.channel_mask, false /*isInput*/,
+                                                   &trackMetadata.channelMask),
+                result);
+        std::vector<std::string> strTags = HidlUtils::splitAudioTags(halTrackMetadata.tags);
+        if (ignoreNonVendorTags) {
+            strTags = HidlUtils::filterOutNonVendorTags(strTags);
+        }
+        CONVERT_CHECKED(HidlUtils::audioTagsFromHal(strTags, &trackMetadata.tags), result);
+    }
+#else
+    (void)ignoreNonVendorTags;
+#endif
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataToHal(const SourceMetadata& sourceMetadata,
+                                        std::vector<playback_track_metadata_t>* halTracks) {
+    status_t result = NO_ERROR;
+    if (halTracks != nullptr) {
+        halTracks->reserve(sourceMetadata.tracks.size());
+    }
+    for (auto& trackMetadata : sourceMetadata.tracks) {
+        playback_track_metadata_t halTrackMetadata{.gain = trackMetadata.gain};
+        CONVERT_CHECKED(HidlUtils::audioUsageToHal(trackMetadata.usage, &halTrackMetadata.usage),
+                        result);
+        CONVERT_CHECKED(HidlUtils::audioContentTypeToHal(trackMetadata.contentType,
+                                                         &halTrackMetadata.content_type),
+                        result);
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+
+status_t CoreUtils::sourceMetadataToHalV7(const SourceMetadata& sourceMetadata,
+                                          bool ignoreNonVendorTags,
+                                          std::vector<playback_track_metadata_v7_t>* halTracks) {
+    std::vector<playback_track_metadata_t> bases;
+    status_t result = sourceMetadataToHal(sourceMetadata, halTracks != nullptr ? &bases : nullptr);
+    if (halTracks != nullptr) {
+        halTracks->reserve(sourceMetadata.tracks.size());
+    }
+    for (size_t i = 0; i < sourceMetadata.tracks.size(); ++i) {
+        playback_track_metadata_v7_t halTrackMetadata;
+        if (halTracks != nullptr) {
+            playback_track_metadata_to_v7(&halTrackMetadata, &bases[i]);
+        }
+#if MAJOR_VERSION >= 7
+        const auto& trackMetadata = sourceMetadata.tracks[i];
+        CONVERT_CHECKED(HidlUtils::audioChannelMaskToHal(trackMetadata.channelMask,
+                                                         &halTrackMetadata.channel_mask),
+                        result);
+        if (ignoreNonVendorTags) {
+            CONVERT_CHECKED(
+                    HidlUtils::audioTagsToHal(HidlUtils::filterOutNonVendorTags(trackMetadata.tags),
+                                              halTrackMetadata.tags),
+                    result);
+        } else {
+            CONVERT_CHECKED(HidlUtils::audioTagsToHal(trackMetadata.tags, halTrackMetadata.tags),
+                            result);
+        }
+#else
+        (void)ignoreNonVendorTags;
+#endif
+        if (halTracks != nullptr) {
+            halTracks->push_back(std::move(halTrackMetadata));
+        }
+    }
+    return result;
+}
+#endif  // MAJOR_VERSION >= 4
+
+#if MAJOR_VERSION >= 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+status_t CoreUtils::audioInputFlagsFromHal(audio_input_flags_t halFlagMask,
+                                           AudioInputFlags* flags) {
+    status_t status = NO_ERROR;
+    std::vector<AudioInOutFlag> result;
+    for (uint32_t bit = 0; halFlagMask != 0 && bit < sizeof(audio_input_flags_t) * 8; ++bit) {
+        audio_input_flags_t flag = static_cast<audio_input_flags_t>(1u << bit);
+        if ((flag & halFlagMask) == flag) {
+            AudioInOutFlag flagStr = audio_input_flag_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioInOutFlag(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio input flag value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+            halFlagMask = static_cast<audio_input_flags_t>(halFlagMask & ~flag);
+        }
+    }
+    *flags = result;
+    return status;
+}
+
+status_t CoreUtils::audioInputFlagsToHal(const AudioInputFlags& flags,
+                                         audio_input_flags_t* halFlagMask) {
+    status_t status = NO_ERROR;
+    *halFlagMask = {};
+    for (const auto& flag : flags) {
+        audio_input_flags_t halFlag;
+        if (!xsd::isUnknownAudioInOutFlag(flag) &&
+            audio_input_flag_from_string(flag.c_str(), &halFlag)) {
+            *halFlagMask = static_cast<audio_input_flags_t>(*halFlagMask | halFlag);
+        } else {
+            ALOGE("Unknown audio input flag \"%s\"", flag.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+
+status_t CoreUtils::audioOutputFlagsFromHal(audio_output_flags_t halFlagMask,
+                                            AudioOutputFlags* flags) {
+    status_t status = NO_ERROR;
+    std::vector<AudioInOutFlag> result;
+    for (uint32_t bit = 0; halFlagMask != 0 && bit < sizeof(audio_output_flags_t) * 8; ++bit) {
+        audio_output_flags_t flag = static_cast<audio_output_flags_t>(1u << bit);
+        if ((flag & halFlagMask) == flag) {
+            AudioInOutFlag flagStr = audio_output_flag_to_string(flag);
+            if (!flagStr.empty() && !xsd::isUnknownAudioInOutFlag(flagStr)) {
+                result.push_back(flagStr);
+            } else {
+                ALOGE("Unknown audio output flag value 0x%X", flag);
+                status = BAD_VALUE;
+            }
+            halFlagMask = static_cast<audio_output_flags_t>(halFlagMask & ~flag);
+        }
+    }
+    *flags = result;
+    return status;
+}
+
+status_t CoreUtils::audioOutputFlagsToHal(const AudioOutputFlags& flags,
+                                          audio_output_flags_t* halFlagMask) {
+    status_t status = NO_ERROR;
+    *halFlagMask = {};
+    for (const auto& flag : flags) {
+        audio_output_flags_t halFlag;
+        if (!xsd::isUnknownAudioInOutFlag(flag) &&
+            audio_output_flag_from_string(flag.c_str(), &halFlag)) {
+            *halFlagMask = static_cast<audio_output_flags_t>(*halFlagMask | halFlag);
+        } else {
+            ALOGE("Unknown audio output flag \"%s\"", flag.c_str());
+            status = BAD_VALUE;
+        }
+    }
+    return status;
+}
+#endif
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/core/all-versions/default/util/include/util/CoreUtils.h b/audio/core/all-versions/default/util/include/util/CoreUtils.h
new file mode 100644
index 0000000..1e5272a
--- /dev/null
+++ b/audio/core/all-versions/default/util/include/util/CoreUtils.h
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+// clang-format off
+
+#include <vector>
+
+#include <system/audio.h>
+
+#include <common/all-versions/VersionUtils.h>
+#include <VersionUtils.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace CPP_VERSION {
+namespace implementation {
+
+using ::android::hardware::audio::common::utils::EnumBitfield;
+using ::android::hardware::hidl_vec;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+struct CoreUtils {
+    // Note: the converters for DeviceAddress have to be in CoreUtils for HAL V4
+    // because DeviceAddress used to be defined in the core HAL. For V5 and above
+    // these functions simply delegate to HidlUtils.
+    static status_t deviceAddressToHal(const DeviceAddress& device, audio_devices_t* halDeviceType,
+            char* halDeviceAddress);
+    static status_t deviceAddressFromHal(audio_devices_t halDeviceType, const char* halDeviceAddress,
+            DeviceAddress* device);
+#if MAJOR_VERSION >= 4
+    static status_t microphoneInfoFromHal(const struct audio_microphone_characteristic_t& halMicInfo,
+            MicrophoneInfo* micInfo);
+    static status_t microphoneInfoToHal(const MicrophoneInfo& micInfo, audio_microphone_characteristic_t* halMicInfo);
+    // Note: {Sink|Source}Metadata types are defined in 'common' (since V5), so they can be used
+    // by the BT HAL. However, the converters are defined here, not in HidlUtils to avoid adding
+    // conditionals to handle V4. The converters are only used by 'core' HAL anyways.
+    static status_t sinkMetadataFromHal(const std::vector<record_track_metadata_t>& halTracks,
+            SinkMetadata* sinkMetadata);
+    static status_t sinkMetadataFromHalV7(const std::vector<record_track_metadata_v7_t>& halTracks,
+            bool ignoreNonVendorTags, SinkMetadata* sinkMetadata);
+    static status_t sinkMetadataToHal(const SinkMetadata& sinkMetadata,
+            std::vector<record_track_metadata_t>* halTracks);
+    static status_t sinkMetadataToHalV7(const SinkMetadata& sinkMetadata, bool ignoreNonVendorTags,
+            std::vector<record_track_metadata_v7_t>* halTracks);
+    static status_t sourceMetadataFromHal(const std::vector<playback_track_metadata_t>& halTracks,
+            SourceMetadata* sourceMetadata);
+    static status_t sourceMetadataFromHalV7(const std::vector<playback_track_metadata_v7_t>& halTracks,
+            bool ignoreNonVendorTags, SourceMetadata* sourceMetadata);
+    static status_t sourceMetadataToHal(const SourceMetadata& sourceMetadata,
+            std::vector<playback_track_metadata_t>* halTracks);
+    static status_t sourceMetadataToHalV7(const SourceMetadata& sourceMetadata, bool ignoreNonVendorTags,
+            std::vector<playback_track_metadata_v7_t>* halTracks);
+#endif
+
+#if MAJOR_VERSION <= 6
+    using AudioInputFlags =
+            ::android::hardware::audio::common::CPP_VERSION::implementation::AudioInputFlagBitfield;
+    using AudioOutputFlags =
+            ::android::hardware::audio::common::CPP_VERSION::implementation::AudioOutputFlagBitfield;
+    static inline status_t audioInputFlagsFromHal(audio_input_flags_t halFlagMask, AudioInputFlags* flags) {
+        *flags = EnumBitfield<AudioInputFlag>(halFlagMask);
+        return NO_ERROR;
+    }
+    static inline status_t audioInputFlagsToHal(AudioInputFlags flags, audio_input_flags_t* halFlagMask) {
+        *halFlagMask = static_cast<audio_input_flags_t>(flags);
+        return NO_ERROR;
+    }
+    static inline status_t audioOutputFlagsFromHal(audio_output_flags_t halFlagMask, AudioOutputFlags* flags) {
+        *flags = EnumBitfield<AudioOutputFlag>(halFlagMask);
+        return NO_ERROR;
+    }
+    static inline status_t audioOutputFlagsToHal(AudioOutputFlags flags, audio_output_flags_t* halFlagMask) {
+        *halFlagMask = static_cast<audio_output_flags_t>(flags);
+        return NO_ERROR;
+    }
+#else
+    using AudioInputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
+    using AudioOutputFlags = hidl_vec<::android::hardware::audio::CPP_VERSION::AudioInOutFlag>;
+    static status_t audioInputFlagsFromHal(audio_input_flags_t halFlagMask, AudioInputFlags* flags);
+    static status_t audioInputFlagsToHal(const AudioInputFlags& flags, audio_input_flags_t* halFlagMask);
+    static status_t audioOutputFlagsFromHal(audio_output_flags_t halFlagMask, AudioOutputFlags* flags);
+    static status_t audioOutputFlagsToHal(const AudioOutputFlags& flags, audio_output_flags_t* halFlagMask);
+#endif
+
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/core/all-versions/default/util/tests/coreutils_tests.cpp b/audio/core/all-versions/default/util/tests/coreutils_tests.cpp
new file mode 100644
index 0000000..0c18482
--- /dev/null
+++ b/audio/core/all-versions/default/util/tests/coreutils_tests.cpp
@@ -0,0 +1,525 @@
+/*
+ * 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.
+ */
+
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "CoreUtils_Test"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio.h>
+#include <util/CoreUtils.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::audio::CPP_VERSION::implementation::CoreUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask = AUDIO_CHANNEL_INVALID;
+static constexpr audio_content_type_t kInvalidHalContentType =
+        static_cast<audio_content_type_t>(0xFFFFFFFFU);
+static constexpr audio_devices_t kInvalidHalDevice = static_cast<audio_devices_t>(0xFFFFFFFFU);
+static constexpr audio_input_flags_t kInvalidInputFlags =
+        static_cast<audio_input_flags_t>(0xFFFFFFFFU);
+static constexpr audio_output_flags_t kInvalidOutputFlags =
+        static_cast<audio_output_flags_t>(0xFFFFFFFFU);
+// AUDIO_SOURCE_INVALID is framework-only.
+static constexpr audio_source_t kInvalidHalSource = static_cast<audio_source_t>(-1);
+static constexpr audio_usage_t kInvalidHalUsage = static_cast<audio_usage_t>(0xFFFFFFFFU);
+
+static bool isInputFlag(xsd::AudioInOutFlag flag) {
+    return toString(flag).find("_INPUT_FLAG_") != std::string::npos;
+}
+
+static bool isOutputFlag(xsd::AudioInOutFlag flag) {
+    return toString(flag).find("_OUTPUT_FLAG_") != std::string::npos;
+}
+
+TEST(CoreUtils, ConvertInvalidInputFlagMask) {
+    CoreUtils::AudioInputFlags invalid;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioInputFlagsFromHal(kInvalidInputFlags, &invalid));
+    audio_input_flags_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioInputFlagsToHal(invalid, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertInputFlagMask) {
+    CoreUtils::AudioInputFlags emptyInputFlags;
+    audio_input_flags_t halEmptyInputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsToHal(emptyInputFlags, &halEmptyInputFlags));
+    EXPECT_EQ(AUDIO_INPUT_FLAG_NONE, halEmptyInputFlags);
+    CoreUtils::AudioInputFlags emptyInputFlagsBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioInputFlagsFromHal(halEmptyInputFlags, &emptyInputFlagsBack));
+    EXPECT_EQ(emptyInputFlags, emptyInputFlagsBack);
+    CoreUtils::AudioInputFlags emptyInputFlagsFromNone;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioInputFlagsFromHal(AUDIO_INPUT_FLAG_NONE, &emptyInputFlagsFromNone));
+    EXPECT_EQ(emptyInputFlags, emptyInputFlagsFromNone);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+        if (isInputFlag(enumVal)) {
+            allEnumValues.push_back(toString(enumVal));
+        }
+    }
+    CoreUtils::AudioInputFlags allInputFlags;
+    allInputFlags.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allInputFlags[i] = allEnumValues[i];
+    }
+    audio_input_flags_t halAllInputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsToHal(allInputFlags, &halAllInputFlags));
+    CoreUtils::AudioInputFlags allInputFlagsBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioInputFlagsFromHal(halAllInputFlags, &allInputFlagsBack));
+    EXPECT_EQ(allInputFlags, allInputFlagsBack);
+}
+
+TEST(CoreUtils, ConvertInvalidOutputFlagMask) {
+    CoreUtils::AudioOutputFlags invalid;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioOutputFlagsFromHal(kInvalidOutputFlags, &invalid));
+    audio_output_flags_t halInvalid;
+    invalid.resize(1);
+    invalid[0] = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::audioOutputFlagsToHal(invalid, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertOutputFlagMask) {
+    CoreUtils::AudioOutputFlags emptyOutputFlags;
+    audio_output_flags_t halEmptyOutputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsToHal(emptyOutputFlags, &halEmptyOutputFlags));
+    EXPECT_EQ(AUDIO_OUTPUT_FLAG_NONE, halEmptyOutputFlags);
+    CoreUtils::AudioOutputFlags emptyOutputFlagsBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::audioOutputFlagsFromHal(halEmptyOutputFlags, &emptyOutputFlagsBack));
+    EXPECT_EQ(emptyOutputFlags, emptyOutputFlagsBack);
+    CoreUtils::AudioOutputFlags emptyOutputFlagsFromNone;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsFromHal(AUDIO_OUTPUT_FLAG_NONE,
+                                                           &emptyOutputFlagsFromNone));
+    EXPECT_EQ(emptyOutputFlags, emptyOutputFlagsFromNone);
+
+    std::vector<std::string> allEnumValues;
+    for (const auto enumVal : xsdc_enum_range<xsd::AudioInOutFlag>{}) {
+        if (isOutputFlag(enumVal)) {
+            allEnumValues.push_back(toString(enumVal));
+        }
+    }
+    CoreUtils::AudioOutputFlags allOutputFlags;
+    allOutputFlags.resize(allEnumValues.size());
+    for (size_t i = 0; i < allEnumValues.size(); ++i) {
+        allOutputFlags[i] = allEnumValues[i];
+    }
+    audio_output_flags_t halAllOutputFlags;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsToHal(allOutputFlags, &halAllOutputFlags));
+    CoreUtils::AudioOutputFlags allOutputFlagsBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::audioOutputFlagsFromHal(halAllOutputFlags, &allOutputFlagsBack));
+    EXPECT_EQ(allOutputFlags, allOutputFlagsBack);
+}
+
+static MicrophoneInfo generateValidMicrophoneInfo() {
+    MicrophoneInfo micInfo{};
+    micInfo.deviceAddress.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC);
+    micInfo.channelMapping.resize(1);
+    micInfo.channelMapping[0] = AudioMicrophoneChannelMapping::DIRECT;
+    micInfo.location = AudioMicrophoneLocation::MAINBODY_MOVABLE;
+    micInfo.group = 42;
+    micInfo.indexInTheGroup = 13;
+    micInfo.sensitivity = 65.5;
+    micInfo.maxSpl = 100.5;
+    micInfo.minSpl = 36.6;
+    micInfo.directionality = AudioMicrophoneDirectionality::HYPER_CARDIOID;
+    micInfo.frequencyResponse.resize(1);
+    micInfo.frequencyResponse[0].frequency = 1000;
+    micInfo.frequencyResponse[0].level = 85;
+    micInfo.position.x = 0;
+    micInfo.position.y = 1;
+    micInfo.position.z = 0;
+    micInfo.orientation.x = 0;
+    micInfo.orientation.y = 0;
+    micInfo.orientation.z = 1;
+    return micInfo;
+}
+
+TEST(CoreUtils, ConvertInvalidMicrophoneInfo) {
+    MicrophoneInfo invalid;
+    audio_microphone_characteristic_t halInvalid{};
+    halInvalid.device = kInvalidHalDevice;
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoFromHal(halInvalid, &invalid));
+
+    MicrophoneInfo oversizeDeviceId = generateValidMicrophoneInfo();
+    oversizeDeviceId.deviceId = std::string(AUDIO_MICROPHONE_ID_MAX_LEN + 1, 'A');
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeDeviceId, &halInvalid));
+    MicrophoneInfo invalidDeviceType = generateValidMicrophoneInfo();
+    invalidDeviceType.deviceAddress.deviceType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(invalidDeviceType, &halInvalid));
+    MicrophoneInfo oversizeChannelMapping = generateValidMicrophoneInfo();
+    oversizeChannelMapping.channelMapping.resize(AUDIO_CHANNEL_COUNT_MAX + 1);
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeChannelMapping, &halInvalid));
+    MicrophoneInfo oversizeFrequencyResponses = generateValidMicrophoneInfo();
+    oversizeFrequencyResponses.frequencyResponse.resize(AUDIO_MICROPHONE_MAX_FREQUENCY_RESPONSES +
+                                                        1);
+    EXPECT_EQ(BAD_VALUE, CoreUtils::microphoneInfoToHal(oversizeFrequencyResponses, &halInvalid));
+}
+
+TEST(CoreUtils, ConvertMicrophoneInfo) {
+    MicrophoneInfo micInfo = generateValidMicrophoneInfo();
+    audio_microphone_characteristic_t halMicInfo;
+    EXPECT_EQ(NO_ERROR, CoreUtils::microphoneInfoToHal(micInfo, &halMicInfo));
+    MicrophoneInfo micInfoBack;
+    EXPECT_EQ(NO_ERROR, CoreUtils::microphoneInfoFromHal(halMicInfo, &micInfoBack));
+    EXPECT_EQ(micInfo, micInfoBack);
+}
+
+static RecordTrackMetadata generateMinimalRecordTrackMetadata() {
+    RecordTrackMetadata metadata{};
+    metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT);
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return metadata;
+}
+
+static RecordTrackMetadata generateValidRecordTrackMetadata() {
+    RecordTrackMetadata metadata = generateMinimalRecordTrackMetadata();
+    metadata.tags.resize(1);
+    metadata.tags[0] = "VX_GOOGLE_42";
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
+    metadata.gain = 1.0;
+    return metadata;
+}
+
+static RecordTrackMetadata generateValidRecordTrackMetadataWithDevice() {
+    RecordTrackMetadata metadata = generateValidRecordTrackMetadata();
+    metadata.destination.device({});
+    metadata.destination.device().deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER);
+    return metadata;
+}
+
+using SinkTracks = hidl_vec<RecordTrackMetadata>;
+
+TEST(CoreUtils, ConvertInvalidSinkMetadata) {
+    SinkMetadata invalidSource;
+    invalidSource.tracks = SinkTracks{generateMinimalRecordTrackMetadata()};
+    invalidSource.tracks[0].source = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHal(invalidSource, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidSource,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataToHalV7(invalidSource, true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidDeviceType;
+    invalidDeviceType.tracks = SinkTracks{generateValidRecordTrackMetadataWithDevice()};
+    invalidDeviceType.tracks[0].destination.device().deviceType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHal(invalidDeviceType, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidDeviceType,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidDeviceType,
+                                                        true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidChannelMask;
+    invalidChannelMask.tracks = SinkTracks{generateValidRecordTrackMetadata()};
+    invalidChannelMask.tracks[0].channelMask = "random string";
+    // Channel mask is sliced away by 'sinkMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(invalidChannelMask, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidChannelMask,
+                                                        false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataToHalV7(invalidChannelMask,
+                                                        true /*ignoreNonVendorTags*/, nullptr));
+    SinkMetadata invalidTags;
+    invalidTags.tracks = SinkTracks{generateValidRecordTrackMetadata()};
+    invalidTags.tracks[0].tags[0] = "random string";
+    // Tags are sliced away by 'sinkMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(invalidTags, nullptr));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataToHalV7(invalidTags, false /*ignoreNonVendorTags*/, nullptr));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(invalidTags, true /*ignoreNonVendorTags*/, nullptr));
+
+    // Verify that a default-initialized metadata is valid.
+    std::vector<record_track_metadata_t> halValid(1, record_track_metadata_t{});
+    std::vector<record_track_metadata_v7_t> halValidV7(1, record_track_metadata_v7_t{});
+    SinkMetadata valid;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHal(halValid, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halValidV7, false /*ignoreNonVendorTags*/, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halValidV7, true /*ignoreNonVendorTags*/, &valid));
+
+    std::vector<record_track_metadata_t> halInvalidSource = {{.source = kInvalidHalSource}};
+    std::vector<record_track_metadata_v7_t> halInvalidSourceV7 = {
+            {.base = {.source = kInvalidHalSource}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHal(halInvalidSource, &invalidSource));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidSourceV7, false /*ignoreNonVendorTags*/,
+                                               &invalidSource));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHalV7(
+                                 halInvalidSourceV7, true /*ignoreNonVendorTags*/, &invalidSource));
+    std::vector<record_track_metadata_t> halInvalidDeviceType = {
+            {.dest_device = kInvalidHalDevice}};
+    std::vector<record_track_metadata_v7_t> halInvalidDeviceTypeV7 = {
+            {.base = {.dest_device = kInvalidHalDevice}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHal(halInvalidDeviceType, &invalidDeviceType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidDeviceTypeV7,
+                                               false /*ignoreNonVendorTags*/, &invalidDeviceType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidDeviceTypeV7, true /*ignoreNonVendorTags*/,
+                                               &invalidDeviceType));
+    std::vector<record_track_metadata_v7_t> halInvalidChannelMaskV7 = {
+            {.channel_mask = kInvalidHalChannelMask}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidChannelMaskV7,
+                                               false /*ignoreNonVendorTags*/, &invalidChannelMask));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sinkMetadataFromHalV7(halInvalidChannelMaskV7,
+                                               true /*ignoreNonVendorTags*/, &invalidChannelMask));
+    std::vector<record_track_metadata_v7_t> halInvalidTagsV7 = {{.tags = "random string"}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sinkMetadataFromHalV7(
+                                 halInvalidTagsV7, false /*ignoreNonVendorTags*/, &invalidTags));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(
+                                halInvalidTagsV7, true /*ignoreNonVendorTags*/, &invalidTags));
+}
+
+TEST(CoreUtils, ConvertEmptySinkMetadata) {
+    SinkMetadata emptySinkMetadata;
+    std::vector<record_track_metadata_t> halEmptySinkMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(emptySinkMetadata, &halEmptySinkMetadata));
+    EXPECT_TRUE(halEmptySinkMetadata.empty());
+    SinkMetadata emptySinkMetadataBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHal(halEmptySinkMetadata, &emptySinkMetadataBack));
+    EXPECT_EQ(emptySinkMetadata, emptySinkMetadataBack);
+    std::vector<record_track_metadata_v7_t> halEmptySinkMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(emptySinkMetadata, false /*ignoreNonVendorTags*/,
+                                             &halEmptySinkMetadataV7));
+    EXPECT_TRUE(halEmptySinkMetadataV7.empty());
+    SinkMetadata emptySinkMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(halEmptySinkMetadataV7,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &emptySinkMetadataBackFromV7));
+    EXPECT_EQ(emptySinkMetadata, emptySinkMetadataBackFromV7);
+}
+
+class SinkMetadataConvertTest : public ::testing::TestWithParam<SinkTracks> {};
+
+TEST_P(SinkMetadataConvertTest, ToFromHal) {
+    SinkMetadata sinkMetadata;
+    sinkMetadata.tracks = GetParam();
+    std::vector<record_track_metadata_t> halSinkMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHal(sinkMetadata, &halSinkMetadata));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadata.size());
+    SinkMetadata sinkMetadataBackTrimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHal(halSinkMetadata, &sinkMetadataBackTrimmed));
+    // Can't compare 'sinkMetadata' to 'sinkMetadataBackTrimmed'
+    std::vector<record_track_metadata_v7_t> halSinkMetadataV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataToHalV7(sinkMetadata, false /*ignoreNonVendorTags*/,
+                                                       &halSinkMetadataV7));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadataV7.size());
+    SinkMetadata sinkMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataFromHalV7(halSinkMetadataV7, false /*ignoreNonVendorTags*/,
+                                               &sinkMetadataBackFromV7));
+    EXPECT_EQ(sinkMetadata, sinkMetadataBackFromV7);
+    std::vector<record_track_metadata_v7_t> halSinkMetadataV7FromTrimmed;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sinkMetadataToHalV7(sinkMetadataBackTrimmed, false /*ignoreNonVendorTags*/,
+                                             &halSinkMetadataV7FromTrimmed));
+    EXPECT_EQ(sinkMetadata.tracks.size(), halSinkMetadataV7FromTrimmed.size());
+    SinkMetadata sinkMetadataBackFromV7Trimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sinkMetadataFromHalV7(halSinkMetadataV7FromTrimmed,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &sinkMetadataBackFromV7Trimmed));
+    EXPECT_EQ(sinkMetadataBackTrimmed, sinkMetadataBackFromV7Trimmed);
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidRecordTrackMetadatas, SinkMetadataConvertTest,
+                         ::testing::Values(SinkTracks{generateMinimalRecordTrackMetadata()},
+                                           SinkTracks{generateValidRecordTrackMetadata()},
+                                           SinkTracks{generateValidRecordTrackMetadataWithDevice()},
+                                           SinkTracks{
+                                                   generateMinimalRecordTrackMetadata(),
+                                                   generateValidRecordTrackMetadata(),
+                                                   generateValidRecordTrackMetadataWithDevice()}));
+
+static PlaybackTrackMetadata generateMinimalPlaybackTrackMetadata() {
+    PlaybackTrackMetadata metadata{};
+    metadata.usage = toString(xsd::AudioUsage::AUDIO_USAGE_UNKNOWN);
+    metadata.contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN);
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_NONE);
+    return metadata;
+}
+
+static PlaybackTrackMetadata generateValidPlaybackTrackMetadata() {
+    PlaybackTrackMetadata metadata = generateMinimalPlaybackTrackMetadata();
+    metadata.tags.resize(1);
+    metadata.tags[0] = "VX_GOOGLE_42";
+    metadata.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO);
+    metadata.gain = 1.0;
+    return metadata;
+}
+
+using SourceTracks = hidl_vec<PlaybackTrackMetadata>;
+
+TEST(CoreUtils, ConvertInvalidSourceMetadata) {
+    SourceMetadata invalidUsage;
+    invalidUsage.tracks = SourceTracks{generateMinimalPlaybackTrackMetadata()};
+    invalidUsage.tracks[0].usage = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHal(invalidUsage, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidUsage,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidContentType;
+    invalidContentType.tracks = SourceTracks{generateMinimalPlaybackTrackMetadata()};
+    invalidContentType.tracks[0].contentType = "random string";
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHal(invalidContentType, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidContentType,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidContentType,
+                                                          true /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidChannelMask;
+    invalidChannelMask.tracks = SourceTracks{generateValidPlaybackTrackMetadata()};
+    invalidChannelMask.tracks[0].channelMask = "random string";
+    // Channel mask is sliced away by 'sourceMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(invalidChannelMask, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidChannelMask,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidChannelMask,
+                                                          true /*ignoreNonVendorTags*/, nullptr));
+    SourceMetadata invalidTags;
+    invalidTags.tracks = SourceTracks{generateValidPlaybackTrackMetadata()};
+    invalidTags.tracks[0].tags[0] = "random string";
+    // Tags are sliced away by 'sourceMetadataToHal'
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(invalidTags, nullptr));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataToHalV7(invalidTags,
+                                                          false /*ignoreNonVendorTags*/, nullptr));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(invalidTags, true /*ignoreNonVendorTags*/, nullptr));
+
+    // Verify that a default-initialized metadata is valid.
+    std::vector<playback_track_metadata_t> halValid(1, playback_track_metadata_t{});
+    std::vector<playback_track_metadata_v7_t> halValidV7(1, playback_track_metadata_v7_t{});
+    SourceMetadata valid;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHal(halValid, &valid));
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halValidV7,
+                                                           false /*ignoreNonVendorTags*/, &valid));
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHalV7(halValidV7, true /*ignoreNonVendorTags*/, &valid));
+
+    std::vector<playback_track_metadata_t> halInvalidUsage = {{.usage = kInvalidHalUsage}};
+    std::vector<playback_track_metadata_v7_t> halInvalidUsageV7 = {
+            {.base = {.usage = kInvalidHalUsage}}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHal(halInvalidUsage, &invalidUsage));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidUsageV7, false /*ignoreNonVendorTags*/, &invalidUsage));
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidUsageV7, true /*ignoreNonVendorTags*/, &invalidUsage));
+    std::vector<playback_track_metadata_t> halInvalidContentType = {
+            {.content_type = kInvalidHalContentType}};
+    std::vector<playback_track_metadata_v7_t> halInvalidContentTypeV7 = {
+            {.base = {.content_type = kInvalidHalContentType}}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHal(halInvalidContentType, &invalidContentType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidContentTypeV7, false /*ignoreNonVendorTags*/, &invalidContentType));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidContentTypeV7, true /*ignoreNonVendorTags*/, &invalidContentType));
+    std::vector<playback_track_metadata_v7_t> halInvalidChannelMaskV7 = {
+            {.channel_mask = kInvalidHalChannelMask}};
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidChannelMaskV7, false /*ignoreNonVendorTags*/, &invalidChannelMask));
+    EXPECT_EQ(BAD_VALUE,
+              CoreUtils::sourceMetadataFromHalV7(
+                      halInvalidChannelMaskV7, true /*ignoreNonVendorTags*/, &invalidChannelMask));
+    std::vector<playback_track_metadata_v7_t> halInvalidTagsV7 = {{.tags = "random string"}};
+    EXPECT_EQ(BAD_VALUE, CoreUtils::sourceMetadataFromHalV7(
+                                 halInvalidTagsV7, false /*ignoreNonVendorTags*/, &invalidTags));
+    // Non-vendor tags should be filtered out.
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(
+                                halInvalidTagsV7, true /*ignoreNonVendorTags*/, &invalidTags));
+}
+
+TEST(CoreUtils, ConvertEmptySourceMetadata) {
+    SourceMetadata emptySourceMetadata;
+    std::vector<playback_track_metadata_t> halEmptySourceMetadata;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHal(emptySourceMetadata, &halEmptySourceMetadata));
+    EXPECT_TRUE(halEmptySourceMetadata.empty());
+    SourceMetadata emptySourceMetadataBack;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHal(halEmptySourceMetadata, &emptySourceMetadataBack));
+    EXPECT_EQ(emptySourceMetadata, emptySourceMetadataBack);
+    std::vector<playback_track_metadata_v7_t> halEmptySourceMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(emptySourceMetadata, false /*ignoreNonVendorTags*/,
+                                               &halEmptySourceMetadataV7));
+    EXPECT_TRUE(halEmptySourceMetadataV7.empty());
+    SourceMetadata emptySourceMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halEmptySourceMetadataV7,
+                                                           false /*ignoreNonVendorTags*/,
+                                                           &emptySourceMetadataBackFromV7));
+    EXPECT_EQ(emptySourceMetadata, emptySourceMetadataBackFromV7);
+}
+
+class SourceMetadataConvertTest : public ::testing::TestWithParam<SourceTracks> {};
+
+TEST_P(SourceMetadataConvertTest, ToFromHal) {
+    SourceMetadata sourceMetadata;
+    sourceMetadata.tracks = GetParam();
+    std::vector<playback_track_metadata_t> halSourceMetadata;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHal(sourceMetadata, &halSourceMetadata));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadata.size());
+    SourceMetadata sourceMetadataBackTrimmed;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHal(halSourceMetadata, &sourceMetadataBackTrimmed));
+    // Can't compare 'sourceMetadata' to 'sourceMetadataBackTrimmed'
+    std::vector<playback_track_metadata_v7_t> halSourceMetadataV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataToHalV7(sourceMetadata, false /*ignoreNonVendorTags*/,
+                                               &halSourceMetadataV7));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadataV7.size());
+    SourceMetadata sourceMetadataBackFromV7;
+    EXPECT_EQ(NO_ERROR,
+              CoreUtils::sourceMetadataFromHalV7(halSourceMetadataV7, false /*ignoreNonVendorTags*/,
+                                                 &sourceMetadataBackFromV7));
+    EXPECT_EQ(sourceMetadata, sourceMetadataBackFromV7);
+    std::vector<playback_track_metadata_v7_t> halSourceMetadataV7FromTrimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataToHalV7(sourceMetadataBackTrimmed,
+                                                         false /*ignoreNonVendorTags*/,
+                                                         &halSourceMetadataV7FromTrimmed));
+    EXPECT_EQ(sourceMetadata.tracks.size(), halSourceMetadataV7FromTrimmed.size());
+    SourceMetadata sourceMetadataBackFromV7Trimmed;
+    EXPECT_EQ(NO_ERROR, CoreUtils::sourceMetadataFromHalV7(halSourceMetadataV7FromTrimmed,
+                                                           false /*ignoreNonVendorTags*/,
+                                                           &sourceMetadataBackFromV7Trimmed));
+    EXPECT_EQ(sourceMetadataBackTrimmed, sourceMetadataBackFromV7Trimmed);
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidPlaybackTrackMetadatas, SourceMetadataConvertTest,
+                         ::testing::Values(SourceTracks{generateMinimalPlaybackTrackMetadata()},
+                                           SourceTracks{generateValidPlaybackTrackMetadata()},
+                                           SourceTracks{generateMinimalPlaybackTrackMetadata(),
+                                                        generateValidPlaybackTrackMetadata()}));
diff --git a/audio/core/all-versions/vts/OWNERS b/audio/core/all-versions/vts/OWNERS
deleted file mode 100644
index 0ea4666..0000000
--- a/audio/core/all-versions/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-elaurent@google.com
-krocard@google.com
-mnaganov@google.com
-yim@google.com
-zhuoyao@google.com
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
index b0eb2e0..b96cc83 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalTest.cpp
@@ -16,6 +16,13 @@
 
 #include "AudioPrimaryHidlHalTest.h"
 
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0.h>
+#include <xsdc/XsdcSupport.h>
+
+using android::xsdc_enum_range;
+#endif
+
 TEST_P(AudioHidlTest, OpenPrimaryDeviceUsingGetDevice) {
     doc::test("Calling openDevice(\"primary\") should return the primary device.");
     if (getDeviceName() != DeviceManager::kPrimaryDevice) {
@@ -53,14 +60,30 @@
             "Make sure getMicrophones always succeeds"
             "and getActiveMicrophones always succeeds when recording from these microphones.");
         AudioConfig config{};
+#if MAJOR_VERSION <= 6
         config.channelMask = mkEnumBitfield(AudioChannelMask::IN_MONO);
         config.sampleRateHz = 8000;
         config.format = AudioFormat::PCM_16_BIT;
         auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
         const SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
-        EventFlag* efGroup;
+#elif MAJOR_VERSION >= 7
+        config.base.channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO);
+        config.base.sampleRateHz = 8000;
+        config.base.format = toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT);
+        hidl_vec<hidl_string> flags;
+        const SinkMetadata initMetadata = {
+                {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_MIC),
+                  .gain = 1,
+                  .tags = {},
+                  .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
+#endif
         for (auto microphone : microphones) {
+#if MAJOR_VERSION <= 6
             if (microphone.deviceAddress.device != AudioDevice::IN_BUILTIN_MIC) {
+#elif MAJOR_VERSION >= 7
+            if (xsd::stringToAudioDevice(microphone.deviceAddress.deviceType) !=
+                xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC) {
+#endif
                 continue;
             }
             sp<IStreamIn> stream;
@@ -72,57 +95,42 @@
                                                             config, flags, initMetadata, cb);
                     },
                     config, &res, &suggestedConfig));
+            StreamReader reader(stream.get(), stream->getBufferSize());
+            ASSERT_TRUE(reader.start());
+            reader.pause();  // This ensures that at least one read has happened.
+            EXPECT_FALSE(reader.hasError());
+
             hidl_vec<MicrophoneInfo> activeMicrophones;
-            Result readRes;
-            typedef MessageQueue<IStreamIn::ReadParameters, kSynchronizedReadWrite> CommandMQ;
-            typedef MessageQueue<uint8_t, kSynchronizedReadWrite> DataMQ;
-            std::unique_ptr<CommandMQ> commandMQ;
-            std::unique_ptr<DataMQ> dataMQ;
-            size_t frameSize = stream->getFrameSize();
-            size_t frameCount = stream->getBufferSize() / frameSize;
-            ASSERT_OK(stream->prepareForReading(
-                frameSize, frameCount, [&](auto r, auto& c, auto& d, auto&, auto&) {
-                    readRes = r;
-                    if (readRes == Result::OK) {
-                        commandMQ.reset(new CommandMQ(c));
-                        dataMQ.reset(new DataMQ(d));
-                        if (dataMQ->isValid() && dataMQ->getEventFlagWord()) {
-                            EventFlag::createEventFlag(dataMQ->getEventFlagWord(), &efGroup);
-                        }
-                    }
-                }));
-            ASSERT_OK(readRes);
-            IStreamIn::ReadParameters params;
-            params.command = IStreamIn::ReadCommand::READ;
-            ASSERT_TRUE(commandMQ != nullptr);
-            ASSERT_TRUE(commandMQ->isValid());
-            ASSERT_TRUE(commandMQ->write(&params));
-            efGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
-            uint32_t efState = 0;
-            efGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
-            if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
-                ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones)));
-                ASSERT_OK(res);
-                ASSERT_NE(0U, activeMicrophones.size());
-            }
-            helper.close(true /*clear*/, &res);
+            ASSERT_OK(stream->getActiveMicrophones(returnIn(res, activeMicrophones)));
             ASSERT_OK(res);
-            if (efGroup) {
-                EventFlag::deleteEventFlag(&efGroup);
-            }
+            EXPECT_NE(0U, activeMicrophones.size());
         }
     }
 }
 
 TEST_P(AudioHidlDeviceTest, SetConnectedState) {
     doc::test("Check that the HAL can be notified of device connection and deconnection");
+#if MAJOR_VERSION <= 6
     using AD = AudioDevice;
     for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) {
         SCOPED_TRACE("device=" + ::testing::PrintToString(deviceType));
+#elif MAJOR_VERSION >= 7
+    using AD = xsd::AudioDevice;
+    for (auto deviceType : {AD::AUDIO_DEVICE_OUT_HDMI, AD::AUDIO_DEVICE_OUT_WIRED_HEADPHONE,
+                            AD::AUDIO_DEVICE_IN_USB_HEADSET}) {
+        SCOPED_TRACE("device=" + toString(deviceType));
+#endif
         for (bool state : {true, false}) {
             SCOPED_TRACE("state=" + ::testing::PrintToString(state));
             DeviceAddress address = {};
+#if MAJOR_VERSION <= 6
             address.device = deviceType;
+#elif MAJOR_VERSION >= 7
+            address.deviceType = toString(deviceType);
+            if (deviceType == AD::AUDIO_DEVICE_IN_USB_HEADSET) {
+                address.address.alsa({0, 0});
+            }
+#endif
             auto ret = getDevice()->setConnectedState(address, state);
             ASSERT_TRUE(ret.isOk());
             if (ret == Result::NOT_SUPPORTED) {
@@ -148,7 +156,11 @@
     }
     // The stream was constructed with one device, thus getDevices must only return one
     ASSERT_EQ(1U, devices.size());
+#if MAJOR_VERSION <= 6
     AudioDevice device = devices[0].device;
+#elif MAJOR_VERSION >= 7
+    auto device = devices[0].deviceType;
+#endif
     ASSERT_TRUE(device == expectedDevice)
         << "Expected: " << ::testing::PrintToString(expectedDevice)
         << "\n  Actual: " << ::testing::PrintToString(device);
@@ -156,12 +168,22 @@
 
 TEST_IO_STREAM(GetDevices, "Check that the stream device == the one it was opened with",
                areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
+#if MAJOR_VERSION <= 6
                                           : testGetDevices(stream.get(), address.device))
+#elif MAJOR_VERSION >= 7
+                                          : testGetDevices(stream.get(), address.deviceType))
+#endif
 
 static void testSetDevices(IStream* stream, const DeviceAddress& address) {
     DeviceAddress otherAddress = address;
+#if MAJOR_VERSION <= 6
     otherAddress.device = (address.device & AudioDevice::BIT_IN) == 0 ? AudioDevice::OUT_SPEAKER
                                                                       : AudioDevice::IN_BUILTIN_MIC;
+#elif MAJOR_VERSION >= 7
+    otherAddress.deviceType = xsd::isOutputDevice(address.deviceType)
+                                      ? toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER)
+                                      : toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC);
+#endif
     EXPECT_RESULT(okOrNotSupported, stream->setDevices({otherAddress}));
 
     ASSERT_RESULT(okOrNotSupported,
@@ -185,14 +207,14 @@
 
 TEST_P(InputStreamTest, updateSinkMetadata) {
     doc::test("The HAL should not crash on metadata change");
-
+#if MAJOR_VERSION <= 6
     hidl_enum_range<AudioSource> range;
     // Test all possible track configuration
-    for (AudioSource source : range) {
+    for (auto source : range) {
         for (float volume : {0.0, 0.5, 1.0}) {
             const SinkMetadata metadata = {{{.source = source, .gain = volume}}};
             ASSERT_OK(stream->updateSinkMetadata(metadata))
-                << "source=" << toString(source) << ", volume=" << volume;
+                    << "source=" << toString(source) << ", volume=" << volume;
         }
     }
 
@@ -200,9 +222,30 @@
 
     // Set no metadata as if all stream track had stopped
     ASSERT_OK(stream->updateSinkMetadata({}));
-
     // Restore initial
     ASSERT_OK(stream->updateSinkMetadata(initMetadata));
+
+#elif MAJOR_VERSION >= 7
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioSource> range;
+    // Test all possible track configuration
+    for (auto source : range) {
+        for (float volume : {0.0, 0.5, 1.0}) {
+            const SinkMetadata metadata = {
+                    {{.source = toString(source),
+                      .gain = volume,
+                      .tags = {},
+                      .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
+            ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(metadata))
+                    << "source=" << toString(source) << ", volume=" << volume;
+        }
+    }
+    // Do not test concurrent capture as this is not officially supported
+
+    // Set no metadata as if all stream track had stopped
+    ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata({}));
+    // Restore initial
+    ASSERT_RESULT(okOrNotSupported, stream->updateSinkMetadata(initMetadata));
+#endif
 }
 
 TEST_P(OutputStreamTest, SelectPresentation) {
@@ -212,7 +255,7 @@
 
 TEST_P(OutputStreamTest, updateSourceMetadata) {
     doc::test("The HAL should not crash on metadata change");
-
+#if MAJOR_VERSION <= 6
     hidl_enum_range<AudioUsage> usageRange;
     hidl_enum_range<AudioContentType> contentRange;
     // Test all possible track configuration
@@ -226,19 +269,68 @@
             }
         }
     }
-
     // Set many track of different configuration
+    // clang-format off
     ASSERT_OK(stream->updateSourceMetadata(
         {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 0.1},
           {AudioUsage::VOICE_COMMUNICATION, AudioContentType::SPEECH, 1.0},
           {AudioUsage::ALARM, AudioContentType::SONIFICATION, 0.0},
-          {AudioUsage::ASSISTANT, AudioContentType::UNKNOWN, 0.3}}}));
-
+          {AudioUsage::ASSISTANT, AudioContentType::UNKNOWN, 0.3}}}
+    ));
+    // clang-format on
     // Set no metadata as if all stream track had stopped
     ASSERT_OK(stream->updateSourceMetadata({}));
-
     // Restore initial
     ASSERT_OK(stream->updateSourceMetadata(initMetadata));
+#elif MAJOR_VERSION >= 7
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioUsage> usageRange;
+    xsdc_enum_range<android::audio::policy::configuration::V7_0::AudioContentType> contentRange;
+    // Test all possible track configuration
+    for (auto usage : usageRange) {
+        for (auto content : contentRange) {
+            for (float volume : {0.0, 0.5, 1.0}) {
+                const SourceMetadata metadata = {
+                        {{toString(usage),
+                          toString(content),
+                          volume,
+                          toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+                          {} /* tags */}}};
+                ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(metadata))
+                        << "usage=" << toString(usage) << ", content=" << toString(content)
+                        << ", volume=" << volume;
+            }
+        }
+    }
+    // Set many track of different configuration
+    // clang-format off
+    ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(
+        {{{toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+                      toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+                      0.1, // gain
+                      toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+                      {}}, // tags
+          {toString(xsd::AudioUsage::AUDIO_USAGE_VOICE_COMMUNICATION),
+                      toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SPEECH),
+                      1.0,
+                      toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+                      {}},
+          {toString(xsd::AudioUsage::AUDIO_USAGE_ALARM),
+                      toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_SONIFICATION),
+                      0.0,
+                      toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+                      {}},
+          {toString(xsd::AudioUsage::AUDIO_USAGE_ASSISTANT),
+                      toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_UNKNOWN),
+                      0.3,
+                      toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+                      {}}}}
+    ));
+    // clang-format on
+    // Set no metadata as if all stream track had stopped
+    ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata({}));
+    // Restore initial
+    ASSERT_RESULT(okOrNotSupported, stream->updateSourceMetadata(initMetadata));
+#endif
 }
 
 TEST_P(AudioPrimaryHidlTest, setMode) {
diff --git a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalUtils.h b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalUtils.h
index 7a52d0e..81a1f7b 100644
--- a/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalUtils.h
+++ b/audio/core/all-versions/vts/functional/4.0/AudioPrimaryHidlHalUtils.h
@@ -56,6 +56,7 @@
     }
 };
 
+#if MAJOR_VERSION <= 6
 struct GetSupported {
     static auto getFormat(IStream* stream) {
         auto ret = stream->getFormat();
@@ -80,7 +81,7 @@
         EXPECT_OK(stream->getSupportedFormats(returnIn(capabilities)));
         return Result::OK;
     }
-#elif MAJOR_VERSION >= 6
+#else  // MAJOR_VERSION == 6
     static Result formats(IStream* stream, hidl_vec<AudioFormat>& capabilities) {
         Result res;
         EXPECT_OK(stream->getSupportedFormats(returnIn(res, capabilities)));
@@ -88,6 +89,7 @@
     }
 #endif
 };
+#endif  // MAJOR_VERSION <= 6
 
 template <class T>
 auto dump(T t, hidl_handle handle) {
diff --git a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
index e09eeab..8af4c78 100644
--- a/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
+++ b/audio/core/all-versions/vts/functional/6.0/AudioPrimaryHidlHalTest.cpp
@@ -17,129 +17,39 @@
 // pull in all the <= 5.0 tests
 #include "5.0/AudioPrimaryHidlHalTest.cpp"
 
-const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
-    static std::vector<DeviceConfigParameter> parameters = [] {
-        std::vector<DeviceConfigParameter> result;
-        for (const auto& device : getDeviceParameters()) {
-            auto module =
-                    getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
-            for (const auto& ioProfile : module->getOutputProfiles()) {
-                for (const auto& profile : ioProfile->getAudioProfiles()) {
-                    const auto& channels = profile->getChannels();
-                    const auto& sampleRates = profile->getSampleRates();
-                    auto configs = ConfigHelper::combineAudioConfig(
-                            vector<audio_channel_mask_t>(channels.begin(), channels.end()),
-                            vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
-                            profile->getFormat());
-                    auto flags = ioProfile->getFlags();
-                    for (auto& config : configs) {
-                        // Some combinations of flags declared in the config file require special
-                        // treatment.
-                        if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
-                            config.offloadInfo.sampleRateHz = config.sampleRateHz;
-                            config.offloadInfo.channelMask = config.channelMask;
-                            config.offloadInfo.format = config.format;
-                            config.offloadInfo.streamType = AudioStreamType::MUSIC;
-                            config.offloadInfo.bitRatePerSecond = 320;
-                            config.offloadInfo.durationMicroseconds = -1;
-                            config.offloadInfo.bitWidth = 16;
-                            config.offloadInfo.bufferSize = 256;  // arbitrary value
-                            config.offloadInfo.usage = AudioUsage::MEDIA;
-                            result.emplace_back(device, config,
-                                                AudioOutputFlag(AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
-                                                                AUDIO_OUTPUT_FLAG_DIRECT));
-                        } else {
-                            if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {  // ignore the flag
-                                flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
-                            }
-                            result.emplace_back(device, config, AudioOutputFlag(flags));
-                        }
-                    }
-                }
-            }
-        }
-        return result;
-    }();
-    return parameters;
-}
-
-const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
-    static std::vector<DeviceConfigParameter> parameters = [] {
-        std::vector<DeviceConfigParameter> result;
-        for (const auto& device : getDeviceParameters()) {
-            auto module =
-                    getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
-            for (const auto& ioProfile : module->getInputProfiles()) {
-                for (const auto& profile : ioProfile->getAudioProfiles()) {
-                    const auto& channels = profile->getChannels();
-                    const auto& sampleRates = profile->getSampleRates();
-                    auto configs = ConfigHelper::combineAudioConfig(
-                            vector<audio_channel_mask_t>(channels.begin(), channels.end()),
-                            vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
-                            profile->getFormat());
-                    for (const auto& config : configs) {
-                        result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
-                    }
-                }
-            }
-        }
-        return result;
-    }();
-    return parameters;
-}
-
-TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedOutputStreams) {
-    doc::test("Verify that a device can't be closed if there are streams opened");
-    DeviceAddress address{.device = AudioDevice::OUT_DEFAULT};
-    AudioConfig config{};
-    auto flags = hidl_bitfield<AudioOutputFlag>(AudioOutputFlag::NONE);
-    SourceMetadata initMetadata = {{{AudioUsage::MEDIA, AudioContentType::MUSIC, 1 /* gain */}}};
-    sp<IStreamOut> stream;
-    StreamHelper<IStreamOut> helper(stream);
-    AudioConfig suggestedConfig{};
-    ASSERT_NO_FATAL_FAILURE(helper.open(
-            [&](AudioIoHandle handle, AudioConfig config, auto cb) {
-                return getDevice()->openOutputStream(handle, address, config, flags, initMetadata,
-                                                     cb);
-            },
-            config, &res, &suggestedConfig));
+class SingleConfigOutputStreamTest : public OutputStreamTest {};
+TEST_P(SingleConfigOutputStreamTest, CloseDeviceWithOpenedOutputStreams) {
+    doc::test("Verify that a device can't be closed if there are output streams opened");
+    // Opening of the stream is done in SetUp.
     ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
-    ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+    ASSERT_OK(closeStream(true /*clear*/));
     ASSERT_OK(getDevice()->close());
     ASSERT_TRUE(resetDevice());
 }
+INSTANTIATE_TEST_CASE_P(SingleConfigOutputStream, SingleConfigOutputStreamTest,
+                        ::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+                        &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigOutputStreamTest);
 
-TEST_P(AudioHidlDeviceTest, CloseDeviceWithOpenedInputStreams) {
-    doc::test("Verify that a device can't be closed if there are streams opened");
-    auto module = getCachedPolicyConfig().getModuleFromName(getDeviceName());
-    if (module->getInputProfiles().empty()) {
-        GTEST_SKIP() << "Device doesn't have input profiles";
-    }
-    DeviceAddress address{.device = AudioDevice::IN_DEFAULT};
-    AudioConfig config{};
-    auto flags = hidl_bitfield<AudioInputFlag>(AudioInputFlag::NONE);
-    SinkMetadata initMetadata = {{{.source = AudioSource::MIC, .gain = 1}}};
-    sp<IStreamIn> stream;
-    StreamHelper<IStreamIn> helper(stream);
-    AudioConfig suggestedConfig{};
-    ASSERT_NO_FATAL_FAILURE(helper.open(
-            [&](AudioIoHandle handle, AudioConfig config, auto cb) {
-                return getDevice()->openInputStream(handle, address, config, flags, initMetadata,
-                                                    cb);
-            },
-            config, &res, &suggestedConfig));
+class SingleConfigInputStreamTest : public InputStreamTest {};
+TEST_P(SingleConfigInputStreamTest, CloseDeviceWithOpenedInputStreams) {
+    doc::test("Verify that a device can't be closed if there are input streams opened");
+    // Opening of the stream is done in SetUp.
     ASSERT_RESULT(Result::INVALID_STATE, getDevice()->close());
-    ASSERT_NO_FATAL_FAILURE(helper.close(true /*clear*/, &res));
+    ASSERT_OK(closeStream(true /*clear*/));
     ASSERT_OK(getDevice()->close());
     ASSERT_TRUE(resetDevice());
 }
+INSTANTIATE_TEST_CASE_P(SingleConfigInputStream, SingleConfigInputStreamTest,
+                        ::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+                        &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SingleConfigInputStreamTest);
 
 TEST_P(AudioPatchHidlTest, UpdatePatchInvalidHandle) {
     doc::test("Verify that passing an invalid handle to updateAudioPatch is checked");
     AudioPatchHandle ignored;
-    ASSERT_OK(getDevice()->updateAudioPatch(
-            static_cast<int32_t>(AudioHandleConsts::AUDIO_PATCH_HANDLE_NONE),
-            hidl_vec<AudioPortConfig>(), hidl_vec<AudioPortConfig>(), returnIn(res, ignored)));
+    ASSERT_OK(getDevice()->updateAudioPatch(AudioPatchHandle{}, hidl_vec<AudioPortConfig>(),
+                                            hidl_vec<AudioPortConfig>(), returnIn(res, ignored)));
     ASSERT_RESULT(Result::INVALID_ARGUMENTS, res);
 }
 
@@ -152,6 +62,7 @@
                             &IStreamOut::setDualMonoMode, &IStreamOut::getDualMonoMode);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DualMonoModeAccessorHidlTest);
 INSTANTIATE_TEST_CASE_P(DualMonoModeHidl, DualMonoModeAccessorHidlTest,
                         ::testing::ValuesIn(getOutputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
@@ -166,6 +77,7 @@
             {48.5f, 1000.0f, std::numeric_limits<float>::infinity()});
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioDescriptionMixLevelHidlTest);
 INSTANTIATE_TEST_CASE_P(AudioDescriptionMixLevelHidl, AudioDescriptionMixLevelHidlTest,
                         ::testing::ValuesIn(getOutputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
@@ -200,6 +112,7 @@
                           TimestretchFallbackMode::FAIL}});
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PlaybackRateParametersHidlTest);
 INSTANTIATE_TEST_CASE_P(PlaybackRateParametersHidl, PlaybackRateParametersHidlTest,
                         ::testing::ValuesIn(getOutputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
diff --git a/audio/core/all-versions/vts/functional/6.0/Generators.cpp b/audio/core/all-versions/vts/functional/6.0/Generators.cpp
new file mode 100644
index 0000000..6b4dbc1
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/6.0/Generators.cpp
@@ -0,0 +1,129 @@
+/*
+ * 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.
+ */
+
+#include <android-base/macros.h>
+
+#include "6.0/Generators.h"
+#include "ConfigHelper.h"
+#include "PolicyConfig.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+// Forward declaration for functions that are substituted
+// in generator unit tests.
+const PolicyConfig& getCachedPolicyConfig();
+const std::vector<DeviceParameter>& getDeviceParameters();
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+
+std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool oneProfilePerDevice) {
+    std::vector<DeviceConfigParameter> result;
+    for (const auto& device : getDeviceParameters()) {
+        auto module =
+                getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+        for (const auto& ioProfile : module->getOutputProfiles()) {
+            for (const auto& profile : ioProfile->getAudioProfiles()) {
+                const auto& channels = profile->getChannels();
+                const auto& sampleRates = profile->getSampleRates();
+                auto configs = ConfigHelper::combineAudioConfig(
+                        std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
+                        std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
+                        profile->getFormat());
+                auto flags = ioProfile->getFlags();
+                for (auto& config : configs) {
+                    // Some combinations of flags declared in the config file require special
+                    // treatment.
+                    if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
+                        config.offloadInfo.sampleRateHz = config.sampleRateHz;
+                        config.offloadInfo.channelMask = config.channelMask;
+                        config.offloadInfo.format = config.format;
+                        config.offloadInfo.streamType = AudioStreamType::MUSIC;
+                        config.offloadInfo.bitRatePerSecond = 320;
+                        config.offloadInfo.durationMicroseconds = -1;
+                        config.offloadInfo.bitWidth = 16;
+                        config.offloadInfo.bufferSize = 256;  // arbitrary value
+                        config.offloadInfo.usage = AudioUsage::MEDIA;
+                        result.emplace_back(device, config,
+                                            AudioOutputFlag(AudioOutputFlag::COMPRESS_OFFLOAD |
+                                                            AudioOutputFlag::DIRECT));
+                    } else {
+                        if (flags & AUDIO_OUTPUT_FLAG_PRIMARY) {  // ignore the flag
+                            flags &= ~AUDIO_OUTPUT_FLAG_PRIMARY;
+                        }
+                        result.emplace_back(device, config, AudioOutputFlag(flags));
+                    }
+                    if (oneProfilePerDevice) break;
+                }
+                if (oneProfilePerDevice) break;
+            }
+            if (oneProfilePerDevice) break;
+        }
+    }
+    return result;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateOutputDeviceConfigParameters(false);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateOutputDeviceConfigParameters(true);
+    return parameters;
+}
+
+std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneProfilePerDevice) {
+    std::vector<DeviceConfigParameter> result;
+    for (const auto& device : getDeviceParameters()) {
+        auto module =
+                getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+        for (const auto& ioProfile : module->getInputProfiles()) {
+            for (const auto& profile : ioProfile->getAudioProfiles()) {
+                const auto& channels = profile->getChannels();
+                const auto& sampleRates = profile->getSampleRates();
+                auto configs = ConfigHelper::combineAudioConfig(
+                        std::vector<audio_channel_mask_t>(channels.begin(), channels.end()),
+                        std::vector<uint32_t>(sampleRates.begin(), sampleRates.end()),
+                        profile->getFormat());
+                for (const auto& config : configs) {
+                    result.emplace_back(device, config, AudioInputFlag(ioProfile->getFlags()));
+                    if (oneProfilePerDevice) break;
+                }
+                if (oneProfilePerDevice) break;
+            }
+            if (oneProfilePerDevice) break;
+        }
+    }
+    return result;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateInputDeviceConfigParameters(false);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateInputDeviceConfigParameters(true);
+    return parameters;
+}
diff --git a/audio/core/all-versions/vts/functional/6.0/Generators.h b/audio/core/all-versions/vts/functional/6.0/Generators.h
new file mode 100644
index 0000000..1e87163
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/6.0/Generators.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "AudioTestDefinitions.h"
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters();
+const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters();
+
+// For unit tests
+std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool oneProfilePerDevice);
+std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneProfilePerDevice);
diff --git a/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..0b3098b
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,841 @@
+/*
+ * 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-base/chrono_utils.h>
+
+#include "Generators.h"
+
+// pull in all the <= 6.0 tests
+#include "6.0/AudioPrimaryHidlHalTest.cpp"
+
+class InvalidInputConfigNoFlagsTest : public AudioHidlTestWithDeviceConfigParameter {};
+TEST_P(InvalidInputConfigNoFlagsTest, InputBufferSizeTest) {
+    doc::test("Verify that invalid config is rejected by IDevice::getInputBufferSize method.");
+    uint64_t bufferSize;
+    ASSERT_OK(getDevice()->getInputBufferSize(getConfig(), returnIn(res, bufferSize)));
+    EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
+}
+INSTANTIATE_TEST_CASE_P(
+        InputBufferSizeInvalidConfig, InvalidInputConfigNoFlagsTest,
+        ::testing::ValuesIn(getInputDeviceInvalidConfigParameters(false /*generateInvalidFlags*/)),
+        &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InvalidInputConfigNoFlagsTest);
+
+static const DeviceAddress& getValidInputDeviceAddress() {
+    static const DeviceAddress valid = {
+            .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT)};
+    return valid;
+}
+
+static const DeviceAddress& getValidOutputDeviceAddress() {
+    static const DeviceAddress valid = {
+            .deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT)};
+    return valid;
+}
+
+static const DeviceAddress& getInvalidDeviceAddress() {
+    static const DeviceAddress valid = {.deviceType = "random_string"};
+    return valid;
+}
+
+TEST_P(AudioHidlDeviceTest, SetConnectedStateInvalidDeviceAddress) {
+    doc::test("Check that invalid device address is rejected by IDevice::setConnectedState");
+    EXPECT_RESULT(Result::INVALID_ARGUMENTS,
+                  getDevice()->setConnectedState(getInvalidDeviceAddress(), true));
+    EXPECT_RESULT(Result::INVALID_ARGUMENTS,
+                  getDevice()->setConnectedState(getInvalidDeviceAddress(), false));
+}
+
+static std::vector<AudioPortConfig>& generatePortConfigs(bool valid) {
+    enum {  // Note: This is for convenience when deriving "invalid" configs from "valid".
+        PORT_CONF_MINIMAL,
+        PORT_CONF_WITH_GAIN,
+        PORT_CONF_EXT_DEVICE,
+        PORT_CONF_EXT_MIX_SOURCE,
+        PORT_CONF_EXT_MIX_SINK,
+        PORT_CONF_EXT_SESSION
+    };
+    static std::vector<AudioPortConfig> valids = [] {
+        std::vector<AudioPortConfig> result;
+        result.reserve(PORT_CONF_EXT_SESSION + 1);
+        result.push_back(AudioPortConfig{});
+        AudioPortConfig configWithGain{};
+        configWithGain.gain.config(AudioGainConfig{
+                .index = 0,
+                .mode = {toString(xsd::AudioGainMode::AUDIO_GAIN_MODE_JOINT)},
+                .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_MONO),
+                .rampDurationMs = 1});
+        configWithGain.gain.config().values.resize(1);
+        configWithGain.gain.config().values[0] = 1000;
+        result.push_back(std::move(configWithGain));
+        AudioPortConfig configWithPortExtDevice{};
+        configWithPortExtDevice.ext.device(getValidOutputDeviceAddress());
+        result.push_back(std::move(configWithPortExtDevice));
+        AudioPortConfig configWithPortExtMixSource{};
+        configWithPortExtMixSource.ext.mix({});
+        configWithPortExtMixSource.ext.mix().useCase.stream(
+                toString(xsd::AudioStreamType::AUDIO_STREAM_VOICE_CALL));
+        result.push_back(std::move(configWithPortExtMixSource));
+        AudioPortConfig configWithPortExtMixSink{};
+        configWithPortExtMixSink.ext.mix({});
+        configWithPortExtMixSink.ext.mix().useCase.source(
+                toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT));
+        result.push_back(std::move(configWithPortExtMixSink));
+        AudioPortConfig configWithPortExtSession{};
+        configWithPortExtSession.ext.session(
+                static_cast<AudioSession>(AudioSessionConsts::OUTPUT_MIX));
+        result.push_back(std::move(configWithPortExtSession));
+        return result;
+    }();
+    static std::vector<AudioPortConfig> invalids = [&] {
+        std::vector<AudioPortConfig> result;
+        AudioPortConfig invalidBaseChannelMask = valids[PORT_CONF_MINIMAL];
+        invalidBaseChannelMask.base.channelMask.value("random_string");
+        result.push_back(std::move(invalidBaseChannelMask));
+        AudioPortConfig invalidBaseFormat = valids[PORT_CONF_MINIMAL];
+        invalidBaseFormat.base.format.value("random_string");
+        result.push_back(std::move(invalidBaseFormat));
+        AudioPortConfig invalidGainMode = valids[PORT_CONF_WITH_GAIN];
+        invalidGainMode.gain.config().mode = {{"random_string"}};
+        result.push_back(std::move(invalidGainMode));
+        AudioPortConfig invalidGainChannelMask = valids[PORT_CONF_WITH_GAIN];
+        invalidGainChannelMask.gain.config().channelMask = "random_string";
+        result.push_back(std::move(invalidGainChannelMask));
+        AudioPortConfig invalidDeviceType = valids[PORT_CONF_EXT_DEVICE];
+        invalidDeviceType.ext.device().deviceType = "random_string";
+        result.push_back(std::move(invalidDeviceType));
+        AudioPortConfig invalidStreamType = valids[PORT_CONF_EXT_MIX_SOURCE];
+        invalidStreamType.ext.mix().useCase.stream() = "random_string";
+        result.push_back(std::move(invalidStreamType));
+        AudioPortConfig invalidSource = valids[PORT_CONF_EXT_MIX_SINK];
+        invalidSource.ext.mix().useCase.source() = "random_string";
+        result.push_back(std::move(invalidSource));
+        return result;
+    }();
+    return valid ? valids : invalids;
+}
+
+TEST_P(AudioHidlDeviceTest, SetAudioPortConfigInvalidArguments) {
+    doc::test("Check that invalid port configs are rejected by IDevice::setAudioPortConfig");
+    for (const auto& invalidConfig : generatePortConfigs(false /*valid*/)) {
+        EXPECT_RESULT(invalidArgsOrNotSupported, getDevice()->setAudioPortConfig(invalidConfig))
+                << ::testing::PrintToString(invalidConfig);
+    }
+}
+
+TEST_P(AudioPatchHidlTest, CreatePatchInvalidArguments) {
+    doc::test("Check that invalid port configs are rejected by IDevice::createAudioPatch");
+    // Note that HAL actually might reject the proposed source / sink combo
+    // due to other reasons than presence of invalid enum-strings.
+    // TODO: Come up with a way to guarantee validity of a source / sink combo.
+    for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
+        for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
+            AudioPatchHandle handle;
+            EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{validSource},
+                                                    hidl_vec<AudioPortConfig>{invalidSink},
+                                                    returnIn(res, handle)));
+            EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+                    << "Source: " << ::testing::PrintToString(validSource)
+                    << "; Sink: " << ::testing::PrintToString(invalidSink);
+        }
+    }
+    for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
+        for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
+            AudioPatchHandle handle;
+            EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{invalidSource},
+                                                    hidl_vec<AudioPortConfig>{validSink},
+                                                    returnIn(res, handle)));
+            EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+                    << "Source: " << ::testing::PrintToString(invalidSource)
+                    << "; Sink: " << ::testing::PrintToString(validSink);
+        }
+    }
+}
+
+TEST_P(AudioPatchHidlTest, UpdatePatchInvalidArguments) {
+    doc::test("Check that invalid port configs are rejected by IDevice::updateAudioPatch");
+    // Note that HAL actually might reject the proposed source / sink combo
+    // due to other reasons than presence of invalid enum-strings.
+    // TODO: Come up with a way to guarantee validity of a source / sink combo.
+    for (const auto& validSource : generatePortConfigs(true /*valid*/)) {
+        for (const auto& invalidSink : generatePortConfigs(false /*valid*/)) {
+            AudioPatchHandle handle{};
+            EXPECT_OK(getDevice()->updateAudioPatch(handle, hidl_vec<AudioPortConfig>{validSource},
+                                                    hidl_vec<AudioPortConfig>{invalidSink},
+                                                    returnIn(res, handle)));
+            EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+                    << "Source: " << ::testing::PrintToString(validSource)
+                    << "; Sink: " << ::testing::PrintToString(invalidSink);
+        }
+    }
+    for (const auto& validSink : generatePortConfigs(true /*valid*/)) {
+        for (const auto& invalidSource : generatePortConfigs(false /*valid*/)) {
+            AudioPatchHandle handle{};
+            EXPECT_OK(getDevice()->updateAudioPatch(
+                    handle, hidl_vec<AudioPortConfig>{invalidSource},
+                    hidl_vec<AudioPortConfig>{validSink}, returnIn(res, handle)));
+            EXPECT_EQ(Result::INVALID_ARGUMENTS, res)
+                    << "Source: " << ::testing::PrintToString(invalidSource)
+                    << "; Sink: " << ::testing::PrintToString(validSink);
+        }
+    }
+}
+
+enum { PARAM_DEVICE_CONFIG, PARAM_ADDRESS, PARAM_METADATA };
+enum { INDEX_SINK, INDEX_SOURCE };
+using SinkOrSourceMetadata = std::variant<SinkMetadata, SourceMetadata>;
+using StreamOpenParameter = std::tuple<DeviceConfigParameter, DeviceAddress, SinkOrSourceMetadata>;
+
+static std::string StreamOpenParameterToString(
+        const ::testing::TestParamInfo<StreamOpenParameter>& info) {
+    return DeviceConfigParameterToString(::testing::TestParamInfo<DeviceConfigParameter>{
+                   std::get<PARAM_DEVICE_CONFIG>(info.param), info.index}) +
+           "__" +
+           SanitizeStringForGTestName(
+                   ::testing::PrintToString(std::get<PARAM_ADDRESS>(info.param))) +
+           "__" +
+           SanitizeStringForGTestName(std::visit(
+                   [](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
+                   std::get<PARAM_METADATA>(info.param)));
+}
+
+class StreamOpenTest : public HidlTest, public ::testing::WithParamInterface<StreamOpenParameter> {
+  protected:
+    void SetUp() override {
+        ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp());  // setup base
+        ASSERT_TRUE(getDevicesFactory() != nullptr);
+        ASSERT_TRUE(getDevice() != nullptr);
+    }
+    const std::string& getFactoryName() const override {
+        return std::get<PARAM_FACTORY_NAME>(
+                std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
+    }
+    const std::string& getDeviceName() const override {
+        return std::get<PARAM_DEVICE_NAME>(
+                std::get<PARAM_DEVICE>(std::get<PARAM_DEVICE_CONFIG>(GetParam())));
+    }
+    const AudioConfig& getConfig() const {
+        return std::get<PARAM_CONFIG>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
+    }
+    const hidl_vec<AudioInOutFlag> getFlags() const {
+        return std::get<PARAM_FLAGS>(std::get<PARAM_DEVICE_CONFIG>(GetParam()));
+    }
+    const DeviceAddress& getDeviceAddress() const { return std::get<PARAM_ADDRESS>(GetParam()); }
+    bool isParamForInputStream() const {
+        return std::get<PARAM_METADATA>(GetParam()).index() == INDEX_SINK;
+    }
+    const SinkMetadata& getSinkMetadata() const {
+        return std::get<INDEX_SINK>(std::get<PARAM_METADATA>(GetParam()));
+    }
+    const SourceMetadata& getSourceMetadata() const {
+        return std::get<INDEX_SOURCE>(std::get<PARAM_METADATA>(GetParam()));
+    }
+};
+
+static const RecordTrackMetadata& getValidRecordTrackMetadata() {
+    static const RecordTrackMetadata valid = {
+            .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT), .gain = 1};
+    return valid;
+}
+
+static const RecordTrackMetadata& getValidRecordTrackMetadataWithDest() {
+    static const RecordTrackMetadata valid = {
+            .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+            .gain = 1,
+            .destination = [] {
+                RecordTrackMetadata::Destination dest;
+                dest.device(getValidOutputDeviceAddress());
+                return dest;
+            }()};
+    return valid;
+}
+
+static const RecordTrackMetadata& getInvalidSourceRecordTrackMetadata() {
+    static const RecordTrackMetadata invalid = {.source = "random_string", .gain = 1};
+    return invalid;
+}
+
+static const RecordTrackMetadata& getRecordTrackMetadataWithInvalidDest() {
+    static const RecordTrackMetadata invalid = {
+            .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+            .gain = 1,
+            .destination = [] {
+                RecordTrackMetadata::Destination dest;
+                dest.device(getInvalidDeviceAddress());
+                return dest;
+            }()};
+    return invalid;
+}
+
+static const RecordTrackMetadata& getInvalidChannelMaskRecordTrackMetadata() {
+    static const RecordTrackMetadata invalid = {
+            .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+            .gain = 1,
+            .channelMask = "random_string"};
+    return invalid;
+}
+
+static const RecordTrackMetadata& getInvalidTagsRecordTrackMetadata() {
+    static const RecordTrackMetadata invalid = {
+            .source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+            .gain = 1,
+            .tags = {{"random_string"}}};
+    return invalid;
+}
+
+static const PlaybackTrackMetadata& getValidPlaybackTrackMetadata() {
+    static const PlaybackTrackMetadata valid = {
+            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+            .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+            .gain = 1};
+    return valid;
+}
+
+static const PlaybackTrackMetadata& getInvalidUsagePlaybackTrackMetadata() {
+    static const PlaybackTrackMetadata invalid = {
+            .usage = "random_string",
+            .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+            .gain = 1};
+    return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidContentTypePlaybackTrackMetadata() {
+    static const PlaybackTrackMetadata invalid = {
+            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+            .contentType = "random_string",
+            .gain = 1};
+    return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidChannelMaskPlaybackTrackMetadata() {
+    static const PlaybackTrackMetadata invalid = {
+            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+            .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+            .gain = 1,
+            .channelMask = "random_string"};
+    return invalid;
+}
+
+static const PlaybackTrackMetadata& getInvalidTagsPlaybackTrackMetadata() {
+    static const PlaybackTrackMetadata invalid = {
+            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+            .contentType = toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+            .gain = 1,
+            .tags = {{"random_string"}}};
+    return invalid;
+}
+
+static const std::vector<SourceMetadata>& getInvalidSourceMetadatas() {
+    static const std::vector<SourceMetadata> invalids = {
+            SourceMetadata{.tracks = {{getInvalidUsagePlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getInvalidContentTypePlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getInvalidChannelMaskPlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getInvalidTagsPlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+                                       getInvalidUsagePlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+                                       getInvalidContentTypePlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+                                       getInvalidChannelMaskPlaybackTrackMetadata()}}},
+            SourceMetadata{.tracks = {{getValidPlaybackTrackMetadata(),
+                                       getInvalidTagsPlaybackTrackMetadata()}}}};
+    return invalids;
+}
+static const std::vector<SinkMetadata>& getInvalidSinkMetadatas() {
+    static const std::vector<SinkMetadata> invalids = {
+            SinkMetadata{.tracks = {{getInvalidSourceRecordTrackMetadata()}}},
+            SinkMetadata{.tracks = {{getRecordTrackMetadataWithInvalidDest()}}},
+            SinkMetadata{.tracks = {{getInvalidChannelMaskRecordTrackMetadata()}}},
+            SinkMetadata{.tracks = {{getInvalidTagsRecordTrackMetadata()}}},
+            SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+                                     getInvalidSourceRecordTrackMetadata()}}},
+            SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+                                     getRecordTrackMetadataWithInvalidDest()}}},
+            SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+                                     getInvalidChannelMaskRecordTrackMetadata()}}},
+            SinkMetadata{.tracks = {{getValidRecordTrackMetadata(),
+                                     getInvalidTagsRecordTrackMetadata()}}}};
+    return invalids;
+}
+template <typename T>
+static inline std::vector<SinkOrSourceMetadata> wrapMetadata(const std::vector<T>& metadata) {
+    return std::vector<SinkOrSourceMetadata>{metadata.begin(), metadata.end()};
+}
+
+TEST_P(StreamOpenTest, OpenInputOrOutputStreamTest) {
+    doc::test(
+            "Verify that invalid arguments are rejected by "
+            "IDevice::open{Input|Output}Stream method.");
+    AudioConfig suggestedConfig{};
+    if (isParamForInputStream()) {
+        sp<IStreamIn> stream;
+        ASSERT_OK(getDevice()->openInputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
+                                               getFlags(), getSinkMetadata(),
+                                               returnIn(res, stream, suggestedConfig)));
+        ASSERT_TRUE(stream == nullptr);
+    } else {
+        sp<IStreamOut> stream;
+        ASSERT_OK(getDevice()->openOutputStream(AudioIoHandle{}, getDeviceAddress(), getConfig(),
+                                                getFlags(), getSourceMetadata(),
+                                                returnIn(res, stream, suggestedConfig)));
+        ASSERT_TRUE(stream == nullptr);
+    }
+    EXPECT_EQ(Result::INVALID_ARGUMENTS, res);
+    EXPECT_EQ(AudioConfig{}, suggestedConfig);
+}
+INSTANTIATE_TEST_CASE_P(
+        InputStreamInvalidConfig, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getInputDeviceInvalidConfigParameters()),
+                           ::testing::Values(getValidInputDeviceAddress()),
+                           ::testing::Values(SinkMetadata{
+                                   .tracks = {{getValidRecordTrackMetadata(),
+                                               getValidRecordTrackMetadataWithDest()}}})),
+        &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+        InputStreamInvalidAddress, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+                           ::testing::Values(getInvalidDeviceAddress()),
+                           ::testing::Values(SinkMetadata{
+                                   .tracks = {{getValidRecordTrackMetadata(),
+                                               getValidRecordTrackMetadataWithDest()}}})),
+        &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+        InputStreamInvalidMetadata, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getInputDeviceSingleConfigParameters()),
+                           ::testing::Values(getValidInputDeviceAddress()),
+                           ::testing::ValuesIn(wrapMetadata(getInvalidSinkMetadatas()))),
+        &StreamOpenParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StreamOpenTest);
+
+INSTANTIATE_TEST_CASE_P(
+        OutputStreamInvalidConfig, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getOutputDeviceInvalidConfigParameters()),
+                           ::testing::Values(getValidOutputDeviceAddress()),
+                           ::testing::Values(SourceMetadata{
+                                   .tracks = {{getValidPlaybackTrackMetadata()}}})),
+        &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+        OutputStreamInvalidAddress, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+                           ::testing::Values(getInvalidDeviceAddress()),
+                           ::testing::Values(SourceMetadata{
+                                   .tracks = {{getValidPlaybackTrackMetadata()}}})),
+        &StreamOpenParameterToString);
+INSTANTIATE_TEST_CASE_P(
+        OutputStreamInvalidMetadata, StreamOpenTest,
+        ::testing::Combine(::testing::ValuesIn(getOutputDeviceSingleConfigParameters()),
+                           ::testing::Values(getValidOutputDeviceAddress()),
+                           ::testing::ValuesIn(wrapMetadata(getInvalidSourceMetadatas()))),
+        &StreamOpenParameterToString);
+
+#define TEST_SINGLE_CONFIG_IO_STREAM(test_name, documentation, code) \
+    TEST_P(SingleConfigInputStreamTest, test_name) {                 \
+        doc::test(documentation);                                    \
+        code;                                                        \
+    }                                                                \
+    TEST_P(SingleConfigOutputStreamTest, test_name) {                \
+        doc::test(documentation);                                    \
+        code;                                                        \
+    }
+
+static void testSetDevicesInvalidDeviceAddress(IStream* stream) {
+    ASSERT_RESULT(Result::INVALID_ARGUMENTS, stream->setDevices({getInvalidDeviceAddress()}));
+}
+TEST_SINGLE_CONFIG_IO_STREAM(
+        SetDevicesInvalidDeviceAddress,
+        "Verify that invalid device address is rejected by IStream::setDevices",
+        areAudioPatchesSupported() ? doc::partialTest("Audio patches are supported")
+                                   : testSetDevicesInvalidDeviceAddress(stream.get()));
+
+static void testSetAudioPropertiesInvalidArguments(IStream* stream) {
+    AudioConfigBaseOptional invalidFormat;
+    invalidFormat.format.value("random_string");
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setAudioProperties(invalidFormat));
+
+    AudioConfigBaseOptional invalidChannelMask;
+    invalidChannelMask.channelMask.value("random_string");
+    ASSERT_RESULT(invalidArgsOrNotSupported, stream->setAudioProperties(invalidChannelMask));
+}
+TEST_SINGLE_CONFIG_IO_STREAM(
+        SetAudioPropertiesInvalidArguments,
+        "Verify that invalid arguments are rejected by IStream::setAudioProperties",
+        testSetAudioPropertiesInvalidArguments(stream.get()));
+
+TEST_P(SingleConfigOutputStreamTest, UpdateInvalidSourceMetadata) {
+    doc::test("Verify that invalid metadata is rejected by IStreamOut::updateSourceMetadata");
+    for (const auto& metadata : getInvalidSourceMetadatas()) {
+        ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSourceMetadata(metadata))
+                << ::testing::PrintToString(metadata);
+    }
+}
+
+TEST_P(SingleConfigInputStreamTest, UpdateInvalidSinkMetadata) {
+    doc::test("Verify that invalid metadata is rejected by IStreamIn::updateSinkMetadata");
+    for (const auto& metadata : getInvalidSinkMetadatas()) {
+        ASSERT_RESULT(invalidArgsOrNotSupported, stream->updateSinkMetadata(metadata))
+                << ::testing::PrintToString(metadata);
+    }
+}
+
+static const std::vector<DeviceConfigParameter>& getOutputDevicePcmOnlyConfigParameters() {
+    static const std::vector<DeviceConfigParameter> parameters = [] {
+        auto allParams = getOutputDeviceConfigParameters();
+        std::vector<DeviceConfigParameter> pcmParams;
+        std::copy_if(allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
+            const auto& flags = std::get<PARAM_FLAGS>(cfg);
+            return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
+                   // MMAP NOIRQ and HW A/V Sync profiles use special writing protocols.
+                   &&
+                   std::find_if(flags.begin(), flags.end(),
+                                [](const auto& flag) {
+                                    return flag == toString(xsd::AudioInOutFlag::
+                                                                    AUDIO_OUTPUT_FLAG_MMAP_NOIRQ) ||
+                                           flag == toString(xsd::AudioInOutFlag::
+                                                                    AUDIO_OUTPUT_FLAG_HW_AV_SYNC);
+                                }) == flags.end() &&
+                   !getCachedPolicyConfig()
+                            .getAttachedSinkDeviceForMixPort(
+                                    std::get<PARAM_DEVICE_NAME>(std::get<PARAM_DEVICE>(cfg)),
+                                    std::get<PARAM_PORT_NAME>(cfg))
+                            .empty();
+        });
+        return pcmParams;
+    }();
+    return parameters;
+}
+
+class PcmOnlyConfigOutputStreamTest : public OutputStreamTest {
+  public:
+    void TearDown() override {
+        releasePatchIfNeeded();
+        OutputStreamTest::TearDown();
+    }
+
+    bool canQueryPresentationPosition() const {
+        auto maybeSinkAddress =
+                getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
+        // Returning 'true' when no sink is found so the test can fail later with a more clear
+        // problem description.
+        return !maybeSinkAddress.has_value() ||
+               !xsd::isTelephonyDevice(maybeSinkAddress.value().deviceType);
+    }
+
+    void createPatchIfNeeded() {
+        auto maybeSinkAddress =
+                getCachedPolicyConfig().getSinkDeviceForMixPort(getDeviceName(), getMixPortName());
+        ASSERT_TRUE(maybeSinkAddress.has_value())
+                << "No sink device found for mix port " << getMixPortName() << " (module "
+                << getDeviceName() << ")";
+        if (areAudioPatchesSupported()) {
+            AudioPortConfig source;
+            source.base.format.value(getConfig().base.format);
+            source.base.sampleRateHz.value(getConfig().base.sampleRateHz);
+            source.base.channelMask.value(getConfig().base.channelMask);
+            source.ext.mix({});
+            source.ext.mix().ioHandle = helper.getIoHandle();
+            source.ext.mix().useCase.stream({});
+            AudioPortConfig sink;
+            sink.ext.device(maybeSinkAddress.value());
+            EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
+                                                    hidl_vec<AudioPortConfig>{sink},
+                                                    returnIn(res, mPatchHandle)));
+            mHasPatch = res == Result::OK;
+        } else {
+            EXPECT_OK(stream->setDevices({maybeSinkAddress.value()}));
+        }
+    }
+
+    void releasePatchIfNeeded() {
+        if (areAudioPatchesSupported()) {
+            if (mHasPatch) {
+                EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
+                mHasPatch = false;
+            }
+        } else {
+            EXPECT_OK(stream->setDevices({address}));
+        }
+    }
+
+    const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+
+    void waitForPresentationPositionAdvance(StreamWriter& writer, uint64_t* firstPosition = nullptr,
+                                            uint64_t* lastPosition = nullptr) {
+        static constexpr int kWriteDurationUs = 50 * 1000;
+        static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+        uint64_t framesInitial;
+        TimeSpec ts;
+        // Starting / resuming of streams is asynchronous at HAL level.
+        // Sometimes HAL doesn't have enough information until the audio data actually gets
+        // consumed by the hardware.
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !writer.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kWriteDurationUs);
+            ASSERT_OK(stream->getPresentationPosition(returnIn(res, framesInitial, ts)));
+            ASSERT_RESULT(okOrInvalidState, res);
+        }
+        ASSERT_FALSE(writer.hasError());
+        ASSERT_FALSE(timedOut);
+
+        uint64_t frames = framesInitial;
+        for (android::base::Timer elapsed;
+             frames <= framesInitial && !writer.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kWriteDurationUs);
+            ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, ts)));
+            ASSERT_RESULT(Result::OK, res);
+        }
+        EXPECT_FALSE(timedOut);
+        EXPECT_FALSE(writer.hasError());
+        EXPECT_GT(frames, framesInitial);
+        if (firstPosition) *firstPosition = framesInitial;
+        if (lastPosition) *lastPosition = frames;
+    }
+
+  private:
+    AudioPatchHandle mPatchHandle = {};
+    bool mHasPatch = false;
+};
+
+TEST_P(PcmOnlyConfigOutputStreamTest, Write) {
+    doc::test("Check that output streams opened for PCM output accepts audio data");
+    StreamWriter writer(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(writer.start());
+    EXPECT_TRUE(writer.waitForAtLeastOneCycle());
+}
+
+TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionAdvancesWithWrites) {
+    doc::test("Check that the presentation position advances with writes");
+    if (!canQueryPresentationPosition()) {
+        GTEST_SKIP() << "Presentation position retrieval is not possible";
+    }
+
+    ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+    StreamWriter writer(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(writer.start());
+    ASSERT_TRUE(writer.waitForAtLeastOneCycle());
+    ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer));
+
+    writer.stop();
+    releasePatchIfNeeded();
+}
+
+TEST_P(PcmOnlyConfigOutputStreamTest, PresentationPositionPreservedOnStandby) {
+    doc::test("Check that the presentation position does not reset on standby");
+    if (!canQueryPresentationPosition()) {
+        GTEST_SKIP() << "Presentation position retrieval is not possible";
+    }
+
+    ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+    StreamWriter writer(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(writer.start());
+    ASSERT_TRUE(writer.waitForAtLeastOneCycle());
+
+    uint64_t framesInitial;
+    ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, nullptr, &framesInitial));
+    writer.pause();
+    ASSERT_OK(stream->standby());
+    writer.resume();
+
+    uint64_t frames;
+    ASSERT_NO_FATAL_FAILURE(waitForPresentationPositionAdvance(writer, &frames));
+    EXPECT_GT(frames, framesInitial);
+
+    writer.stop();
+    releasePatchIfNeeded();
+}
+
+INSTANTIATE_TEST_CASE_P(PcmOnlyConfigOutputStream, PcmOnlyConfigOutputStreamTest,
+                        ::testing::ValuesIn(getOutputDevicePcmOnlyConfigParameters()),
+                        &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigOutputStreamTest);
+
+static const std::vector<DeviceConfigParameter>& getInputDevicePcmOnlyConfigParameters() {
+    static const std::vector<DeviceConfigParameter> parameters = [] {
+        auto allParams = getInputDeviceConfigParameters();
+        std::vector<DeviceConfigParameter> pcmParams;
+        std::copy_if(
+                allParams.begin(), allParams.end(), std::back_inserter(pcmParams), [](auto cfg) {
+                    const auto& flags = std::get<PARAM_FLAGS>(cfg);
+                    return xsd::isLinearPcm(std::get<PARAM_CONFIG>(cfg).base.format)
+                           // MMAP NOIRQ profiles use different reading protocol,
+                           // reading h/w hotword might require Soundtrigger to be active.
+                           &&
+                           std::find_if(
+                                   flags.begin(), flags.end(),
+                                   [](const auto& flag) {
+                                       return flag == toString(
+                                                              xsd::AudioInOutFlag::
+                                                                      AUDIO_INPUT_FLAG_MMAP_NOIRQ) ||
+                                              flag == toString(xsd::AudioInOutFlag::
+                                                                       AUDIO_INPUT_FLAG_HW_HOTWORD);
+                                   }) == flags.end() &&
+                           !getCachedPolicyConfig()
+                                    .getAttachedSourceDeviceForMixPort(
+                                            std::get<PARAM_DEVICE_NAME>(
+                                                    std::get<PARAM_DEVICE>(cfg)),
+                                            std::get<PARAM_PORT_NAME>(cfg))
+                                    .empty();
+                });
+        return pcmParams;
+    }();
+    return parameters;
+}
+
+class PcmOnlyConfigInputStreamTest : public InputStreamTest {
+  public:
+    void TearDown() override {
+        releasePatchIfNeeded();
+        InputStreamTest::TearDown();
+    }
+
+    bool canQueryCapturePosition() const {
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        // Returning 'true' when no source is found so the test can fail later with a more clear
+        // problem description.
+        return !maybeSourceAddress.has_value() ||
+               !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType);
+    }
+
+    void createPatchIfNeeded() {
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        ASSERT_TRUE(maybeSourceAddress.has_value())
+                << "No source device found for mix port " << getMixPortName() << " (module "
+                << getDeviceName() << ")";
+        if (areAudioPatchesSupported()) {
+            AudioPortConfig source;
+            source.ext.device(maybeSourceAddress.value());
+            AudioPortConfig sink;
+            sink.base.format.value(getConfig().base.format);
+            sink.base.sampleRateHz.value(getConfig().base.sampleRateHz);
+            sink.base.channelMask.value(getConfig().base.channelMask);
+            sink.ext.mix({});
+            sink.ext.mix().ioHandle = helper.getIoHandle();
+            sink.ext.mix().useCase.source(toString(xsd::AudioSource::AUDIO_SOURCE_MIC));
+            EXPECT_OK(getDevice()->createAudioPatch(hidl_vec<AudioPortConfig>{source},
+                                                    hidl_vec<AudioPortConfig>{sink},
+                                                    returnIn(res, mPatchHandle)));
+            mHasPatch = res == Result::OK;
+        } else {
+            EXPECT_OK(stream->setDevices({maybeSourceAddress.value()}));
+        }
+    }
+
+    void releasePatchIfNeeded() {
+        if (areAudioPatchesSupported()) {
+            if (mHasPatch) {
+                EXPECT_OK(getDevice()->releaseAudioPatch(mPatchHandle));
+                mHasPatch = false;
+            }
+        } else {
+            EXPECT_OK(stream->setDevices({address}));
+        }
+    }
+
+    void waitForCapturePositionAdvance(StreamReader& reader, uint64_t* firstPosition = nullptr,
+                                       uint64_t* lastPosition = nullptr) {
+        static constexpr int kReadDurationUs = 50 * 1000;
+        static constexpr std::chrono::milliseconds kPositionChangeTimeout{10000};
+        uint64_t framesInitial, ts;
+        // Starting / resuming of streams is asynchronous at HAL level.
+        // Sometimes HAL doesn't have enough information until the audio data actually has been
+        // produced by the hardware. Legacy HALs might return NOT_SUPPORTED when they actually
+        // mean INVALID_STATE.
+        bool timedOut = false;
+        res = Result::INVALID_STATE;
+        for (android::base::Timer elapsed;
+             res != Result::OK && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, framesInitial, ts)));
+            ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
+        }
+        ASSERT_FALSE(reader.hasError());
+        ASSERT_FALSE(timedOut);
+
+        uint64_t frames = framesInitial;
+        for (android::base::Timer elapsed;
+             frames <= framesInitial && !reader.hasError() &&
+             !(timedOut = (elapsed.duration() >= kPositionChangeTimeout));) {
+            usleep(kReadDurationUs);
+            ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, ts)));
+            ASSERT_RESULT(Result::OK, res);
+        }
+        EXPECT_FALSE(timedOut);
+        EXPECT_FALSE(reader.hasError());
+        EXPECT_GT(frames, framesInitial);
+        if (firstPosition) *firstPosition = framesInitial;
+        if (lastPosition) *lastPosition = frames;
+    }
+
+  private:
+    AudioPatchHandle mPatchHandle = {};
+    bool mHasPatch = false;
+};
+
+TEST_P(PcmOnlyConfigInputStreamTest, Read) {
+    doc::test("Check that input streams opened for PCM input retrieve audio data");
+    StreamReader reader(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(reader.start());
+    EXPECT_TRUE(reader.waitForAtLeastOneCycle());
+}
+
+TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionAdvancesWithReads) {
+    doc::test("Check that the capture position advances with reads");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
+
+    ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+    StreamReader reader(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(reader.start());
+    EXPECT_TRUE(reader.waitForAtLeastOneCycle());
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader));
+}
+
+TEST_P(PcmOnlyConfigInputStreamTest, CapturePositionPreservedOnStandby) {
+    doc::test("Check that the capture position does not reset on standby");
+    if (!canQueryCapturePosition()) {
+        GTEST_SKIP() << "Capture position retrieval is not possible";
+    }
+
+    ASSERT_NO_FATAL_FAILURE(createPatchIfNeeded());
+    StreamReader reader(stream.get(), stream->getBufferSize());
+    ASSERT_TRUE(reader.start());
+    EXPECT_TRUE(reader.waitForAtLeastOneCycle());
+
+    uint64_t framesInitial;
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, nullptr, &framesInitial));
+    reader.pause();
+    ASSERT_OK(stream->standby());
+    reader.resume();
+
+    uint64_t frames;
+    ASSERT_NO_FATAL_FAILURE(waitForCapturePositionAdvance(reader, &frames, nullptr));
+    EXPECT_GT(frames, framesInitial);
+
+    reader.stop();
+    releasePatchIfNeeded();
+}
+
+INSTANTIATE_TEST_CASE_P(PcmOnlyConfigInputStream, PcmOnlyConfigInputStreamTest,
+                        ::testing::ValuesIn(getInputDevicePcmOnlyConfigParameters()),
+                        &DeviceConfigParameterToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PcmOnlyConfigInputStreamTest);
diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.cpp b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
new file mode 100644
index 0000000..d2ba339
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/Generators.cpp
@@ -0,0 +1,309 @@
+/*
+ * 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.
+ */
+
+#include <android-base/macros.h>
+
+#include "7.0/Generators.h"
+#include "7.0/PolicyConfig.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android_audio_policy_configuration_V7_0.h>
+
+// Forward declaration for functions that are substituted
+// in generator unit tests.
+const PolicyConfig& getCachedPolicyConfig();
+const std::vector<DeviceParameter>& getDeviceParameters();
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+}
+
+static std::vector<AudioConfig> combineAudioConfig(std::vector<xsd::AudioChannelMask> channelMasks,
+                                                   std::vector<int64_t> sampleRates,
+                                                   const std::string& format) {
+    std::vector<AudioConfig> configs;
+    configs.reserve(channelMasks.size() * sampleRates.size());
+    for (auto channelMask : channelMasks) {
+        for (auto sampleRate : sampleRates) {
+            AudioConfig config{};
+            config.base.channelMask = toString(channelMask);
+            config.base.sampleRateHz = sampleRate;
+            config.base.format = format;
+            configs.push_back(config);
+        }
+    }
+    return configs;
+}
+
+static std::tuple<std::vector<AudioInOutFlag>, bool> generateOutFlags(
+        const xsd::MixPorts::MixPort& mixPort) {
+    static const std::vector<AudioInOutFlag> offloadFlags = {
+            toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD),
+            toString(xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_DIRECT)};
+    std::vector<AudioInOutFlag> flags;
+    bool isOffload = false;
+    if (mixPort.hasFlags()) {
+        auto xsdFlags = mixPort.getFlags();
+        isOffload = std::find(xsdFlags.begin(), xsdFlags.end(),
+                              xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) !=
+                    xsdFlags.end();
+        if (!isOffload) {
+            for (auto flag : xsdFlags) {
+                if (flag != xsd::AudioInOutFlag::AUDIO_OUTPUT_FLAG_PRIMARY) {
+                    flags.push_back(toString(flag));
+                }
+            }
+        } else {
+            flags = offloadFlags;
+        }
+    }
+    return {flags, isOffload};
+}
+
+static AudioOffloadInfo generateOffloadInfo(const AudioConfigBase& base) {
+    return AudioOffloadInfo{
+            .base = base,
+            .streamType = toString(xsd::AudioStreamType::AUDIO_STREAM_MUSIC),
+            .usage = toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+            .bitRatePerSecond = 320,
+            .durationMicroseconds = -1,
+            .bitWidth = 16,
+            .bufferSize = 256  // arbitrary value
+    };
+}
+
+std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool oneProfilePerDevice) {
+    std::vector<DeviceConfigParameter> result;
+    for (const auto& device : getDeviceParameters()) {
+        auto module =
+                getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+        if (!module || !module->getFirstMixPorts()) break;
+        for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+            if (mixPort.getRole() != xsd::Role::source) continue;  // not an output profile
+            auto [flags, isOffload] = generateOutFlags(mixPort);
+            for (const auto& profile : mixPort.getProfile()) {
+                auto configs = combineAudioConfig(profile.getChannelMasks(),
+                                                  profile.getSamplingRates(), profile.getFormat());
+                for (auto& config : configs) {
+                    // Some combinations of flags declared in the config file require special
+                    // treatment.
+                    if (isOffload) {
+                        config.offloadInfo.info(generateOffloadInfo(config.base));
+                    }
+                    result.emplace_back(device, mixPort.getName(), config, flags);
+                    if (oneProfilePerDevice) break;
+                }
+                if (oneProfilePerDevice) break;
+            }
+            if (oneProfilePerDevice) break;
+        }
+    }
+    return result;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateOutputDeviceConfigParameters(false);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateOutputDeviceConfigParameters(true);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters(
+        bool generateInvalidFlags) {
+    static std::vector<DeviceConfigParameter> parameters = [&] {
+        std::vector<DeviceConfigParameter> result;
+        for (const auto& device : getDeviceParameters()) {
+            auto module =
+                    getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+            if (!module || !module->getFirstMixPorts()) break;
+            bool hasRegularConfig = false, hasOffloadConfig = false;
+            for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+                if (mixPort.getRole() != xsd::Role::source) continue;  // not an output profile
+                auto [validFlags, isOffload] = generateOutFlags(mixPort);
+                if ((!isOffload && hasRegularConfig) || (isOffload && hasOffloadConfig)) continue;
+                for (const auto& profile : mixPort.getProfile()) {
+                    if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+                        !profile.hasChannelMasks())
+                        continue;
+                    AudioConfigBase validBase = {
+                            profile.getFormat(),
+                            static_cast<uint32_t>(profile.getSamplingRates()[0]),
+                            toString(profile.getChannelMasks()[0])};
+                    {
+                        AudioConfig config{.base = validBase};
+                        config.base.channelMask = "random_string";
+                        if (isOffload) {
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                        }
+                        result.emplace_back(device, mixPort.getName(), config, validFlags);
+                    }
+                    {
+                        AudioConfig config{.base = validBase};
+                        config.base.format = "random_string";
+                        if (isOffload) {
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                        }
+                        result.emplace_back(device, mixPort.getName(), config, validFlags);
+                    }
+                    if (generateInvalidFlags) {
+                        AudioConfig config{.base = validBase};
+                        if (isOffload) {
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                        }
+                        std::vector<AudioInOutFlag> flags = {"random_string", ""};
+                        result.emplace_back(device, mixPort.getName(), config, flags);
+                    }
+                    if (isOffload) {
+                        {
+                            AudioConfig config{.base = validBase};
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                            config.offloadInfo.info().base.channelMask = "random_string";
+                            result.emplace_back(device, mixPort.getName(), config, validFlags);
+                        }
+                        {
+                            AudioConfig config{.base = validBase};
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                            config.offloadInfo.info().base.format = "random_string";
+                            result.emplace_back(device, mixPort.getName(), config, validFlags);
+                        }
+                        {
+                            AudioConfig config{.base = validBase};
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                            config.offloadInfo.info().streamType = "random_string";
+                            result.emplace_back(device, mixPort.getName(), config, validFlags);
+                        }
+                        {
+                            AudioConfig config{.base = validBase};
+                            config.offloadInfo.info(generateOffloadInfo(validBase));
+                            config.offloadInfo.info().usage = "random_string";
+                            result.emplace_back(device, mixPort.getName(), config, validFlags);
+                        }
+                        hasOffloadConfig = true;
+                    } else {
+                        hasRegularConfig = true;
+                    }
+                    break;
+                }
+                if (hasOffloadConfig && hasRegularConfig) break;
+            }
+        }
+        return result;
+    }();
+    return parameters;
+}
+
+std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneProfilePerDevice) {
+    std::vector<DeviceConfigParameter> result;
+    for (const auto& device : getDeviceParameters()) {
+        auto module =
+                getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+        if (!module || !module->getFirstMixPorts()) break;
+        for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+            if (mixPort.getRole() != xsd::Role::sink) continue;  // not an input profile
+            std::vector<AudioInOutFlag> flags;
+            if (mixPort.hasFlags()) {
+                std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
+                               std::back_inserter(flags), [](auto flag) { return toString(flag); });
+            }
+            for (const auto& profile : mixPort.getProfile()) {
+                auto configs = combineAudioConfig(profile.getChannelMasks(),
+                                                  profile.getSamplingRates(), profile.getFormat());
+                for (const auto& config : configs) {
+                    result.emplace_back(device, mixPort.getName(), config, flags);
+                    if (oneProfilePerDevice) break;
+                }
+                if (oneProfilePerDevice) break;
+            }
+            if (oneProfilePerDevice) break;
+        }
+    }
+    return result;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateInputDeviceConfigParameters(false);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters() {
+    static std::vector<DeviceConfigParameter> parameters =
+            generateInputDeviceConfigParameters(true);
+    return parameters;
+}
+
+const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters(
+        bool generateInvalidFlags) {
+    static std::vector<DeviceConfigParameter> parameters = [&] {
+        std::vector<DeviceConfigParameter> result;
+        for (const auto& device : getDeviceParameters()) {
+            auto module =
+                    getCachedPolicyConfig().getModuleFromName(std::get<PARAM_DEVICE_NAME>(device));
+            if (!module || !module->getFirstMixPorts()) break;
+            bool hasConfig = false;
+            for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+                if (mixPort.getRole() != xsd::Role::sink) continue;  // not an input profile
+                std::vector<AudioInOutFlag> validFlags;
+                if (mixPort.hasFlags()) {
+                    std::transform(mixPort.getFlags().begin(), mixPort.getFlags().end(),
+                                   std::back_inserter(validFlags),
+                                   [](auto flag) { return toString(flag); });
+                }
+                for (const auto& profile : mixPort.getProfile()) {
+                    if (!profile.hasFormat() || !profile.hasSamplingRates() ||
+                        !profile.hasChannelMasks())
+                        continue;
+                    AudioConfigBase validBase = {
+                            profile.getFormat(),
+                            static_cast<uint32_t>(profile.getSamplingRates()[0]),
+                            toString(profile.getChannelMasks()[0])};
+                    {
+                        AudioConfig config{.base = validBase};
+                        config.base.channelMask = "random_string";
+                        result.emplace_back(device, mixPort.getName(), config, validFlags);
+                    }
+                    {
+                        AudioConfig config{.base = validBase};
+                        config.base.format = "random_string";
+                        result.emplace_back(device, mixPort.getName(), config, validFlags);
+                    }
+                    if (generateInvalidFlags) {
+                        AudioConfig config{.base = validBase};
+                        std::vector<AudioInOutFlag> flags = {"random_string", ""};
+                        result.emplace_back(device, mixPort.getName(), config, flags);
+                    }
+                    hasConfig = true;
+                    break;
+                }
+                if (hasConfig) break;
+            }
+        }
+        return result;
+    }();
+    return parameters;
+}
diff --git a/audio/core/all-versions/vts/functional/7.0/Generators.h b/audio/core/all-versions/vts/functional/7.0/Generators.h
new file mode 100644
index 0000000..e36cfad
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/Generators.h
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "AudioTestDefinitions.h"
+
+const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getOutputDeviceSingleConfigParameters();
+const std::vector<DeviceConfigParameter>& getOutputDeviceInvalidConfigParameters(
+        bool generateInvalidFlags = true);
+const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
+const std::vector<DeviceConfigParameter>& getInputDeviceSingleConfigParameters();
+const std::vector<DeviceConfigParameter>& getInputDeviceInvalidConfigParameters(
+        bool generateInvalidFlags = true);
+
+// For unit tests
+std::vector<DeviceConfigParameter> generateOutputDeviceConfigParameters(bool oneProfilePerDevice);
+std::vector<DeviceConfigParameter> generateInputDeviceConfigParameters(bool oneProfilePerDevice);
diff --git a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp
new file mode 100644
index 0000000..2988207
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <algorithm>
+
+#include <HidlUtils.h>
+#include <system/audio.h>
+#include <system/audio_config.h>
+
+#include "DeviceManager.h"
+#include "PolicyConfig.h"
+#include "common/all-versions/HidlSupport.h"
+
+using ::android::NO_ERROR;
+using ::android::OK;
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::utils::splitString;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+using Module = Modules::Module;
+}  // namespace xsd
+
+std::string PolicyConfig::getError() const {
+    if (mFilePath.empty()) {
+        return "Could not find " + mConfigFileName +
+               " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
+    } else {
+        return "Invalid config file: " + mFilePath;
+    }
+}
+
+const xsd::Module* PolicyConfig::getModuleFromName(const std::string& name) const {
+    if (mConfig && mConfig->getFirstModules()) {
+        for (const auto& module : mConfig->getFirstModules()->get_module()) {
+            if (module.getName() == name) return &module;
+        }
+    }
+    return nullptr;
+}
+
+std::optional<DeviceAddress> PolicyConfig::getSinkDeviceForMixPort(
+        const std::string& moduleName, const std::string& mixPortName) const {
+    std::string device;
+    if (auto module = getModuleFromName(moduleName); module) {
+        auto possibleDevices = getSinkDevicesForMixPort(moduleName, mixPortName);
+        if (module->hasDefaultOutputDevice() &&
+            possibleDevices.count(module->getDefaultOutputDevice())) {
+            device = module->getDefaultOutputDevice();
+        } else {
+            device = getAttachedSinkDeviceForMixPort(moduleName, mixPortName);
+        }
+    }
+    if (!device.empty()) {
+        return getDeviceAddressOfDevicePort(moduleName, device);
+    }
+    ALOGE("Could not find a route for the mix port \"%s\" in module \"%s\"", mixPortName.c_str(),
+          moduleName.c_str());
+    return std::optional<DeviceAddress>{};
+}
+
+std::optional<DeviceAddress> PolicyConfig::getSourceDeviceForMixPort(
+        const std::string& moduleName, const std::string& mixPortName) const {
+    const std::string device = getAttachedSourceDeviceForMixPort(moduleName, mixPortName);
+    if (!device.empty()) {
+        return getDeviceAddressOfDevicePort(moduleName, device);
+    }
+    ALOGE("Could not find a route for the mix port \"%s\" in module \"%s\"", mixPortName.c_str(),
+          moduleName.c_str());
+    return std::optional<DeviceAddress>{};
+}
+
+bool PolicyConfig::haveInputProfilesInModule(const std::string& name) const {
+    auto module = getModuleFromName(name);
+    if (module && module->getFirstMixPorts()) {
+        for (const auto& mixPort : module->getFirstMixPorts()->getMixPort()) {
+            if (mixPort.getRole() == xsd::Role::sink) return true;
+        }
+    }
+    return false;
+}
+
+// static
+std::string PolicyConfig::findExistingConfigurationFile(const std::string& fileName) {
+    for (const auto& location : android::audio_get_configuration_paths()) {
+        std::string path = location + '/' + fileName;
+        if (access(path.c_str(), F_OK) == 0) {
+            return path;
+        }
+    }
+    return {};
+}
+
+std::string PolicyConfig::findAttachedDevice(const std::vector<std::string>& attachedDevices,
+                                             const std::set<std::string>& possibleDevices) const {
+    for (const auto& device : attachedDevices) {
+        if (possibleDevices.count(device)) return device;
+    }
+    return {};
+}
+
+const std::vector<std::string>& PolicyConfig::getAttachedDevices(
+        const std::string& moduleName) const {
+    static const std::vector<std::string> empty;
+    auto module = getModuleFromName(moduleName);
+    if (module && module->getFirstAttachedDevices()) {
+        return module->getFirstAttachedDevices()->getItem();
+    }
+    return empty;
+}
+
+std::optional<DeviceAddress> PolicyConfig::getDeviceAddressOfDevicePort(
+        const std::string& moduleName, const std::string& devicePortName) const {
+    auto module = getModuleFromName(moduleName);
+    if (module->getFirstDevicePorts()) {
+        const auto& devicePorts = module->getFirstDevicePorts()->getDevicePort();
+        const auto& devicePort = std::find_if(
+                devicePorts.begin(), devicePorts.end(),
+                [&devicePortName](auto dp) { return dp.getTagName() == devicePortName; });
+        if (devicePort != devicePorts.end()) {
+            audio_devices_t halDeviceType;
+            if (HidlUtils::audioDeviceTypeToHal(devicePort->getType(), &halDeviceType) ==
+                NO_ERROR) {
+                // For AOSP device types use the standard parser for the device address.
+                const std::string address =
+                        devicePort->hasAddress() ? devicePort->getAddress() : "";
+                DeviceAddress result;
+                if (HidlUtils::deviceAddressFromHal(halDeviceType, address.c_str(), &result) ==
+                    NO_ERROR) {
+                    return result;
+                }
+            } else if (xsd::isVendorExtension(devicePort->getType())) {
+                DeviceAddress result;
+                result.deviceType = devicePort->getType();
+                if (devicePort->hasAddress()) {
+                    result.address.id(devicePort->getAddress());
+                }
+                return result;
+            }
+        } else {
+            ALOGE("Device port \"%s\" not found in module \"%s\"", devicePortName.c_str(),
+                  moduleName.c_str());
+        }
+    } else {
+        ALOGE("Module \"%s\" has no device ports", moduleName.c_str());
+    }
+    return std::optional<DeviceAddress>{};
+}
+
+std::set<std::string> PolicyConfig::getSinkDevicesForMixPort(const std::string& moduleName,
+                                                             const std::string& mixPortName) const {
+    std::set<std::string> result;
+    auto module = getModuleFromName(moduleName);
+    if (module && module->getFirstRoutes()) {
+        for (const auto& route : module->getFirstRoutes()->getRoute()) {
+            const auto sources = splitString(route.getSources(), ',');
+            if (std::find(sources.begin(), sources.end(), mixPortName) != sources.end()) {
+                result.insert(route.getSink());
+            }
+        }
+    }
+    return result;
+}
+
+std::set<std::string> PolicyConfig::getSourceDevicesForMixPort(
+        const std::string& moduleName, const std::string& mixPortName) const {
+    std::set<std::string> result;
+    auto module = getModuleFromName(moduleName);
+    if (module && module->getFirstRoutes()) {
+        const auto& routes = module->getFirstRoutes()->getRoute();
+        const auto route = std::find_if(routes.begin(), routes.end(), [&mixPortName](auto rte) {
+            return rte.getSink() == mixPortName;
+        });
+        if (route != routes.end()) {
+            const auto sources = splitString(route->getSources(), ',');
+            std::copy(sources.begin(), sources.end(), std::inserter(result, result.end()));
+        }
+    }
+    return result;
+}
+
+void PolicyConfig::init() {
+    if (mConfig) {
+        mStatus = OK;
+        mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
+        if (mConfig->getFirstModules()) {
+            for (const auto& module : mConfig->getFirstModules()->get_module()) {
+                if (module.getFirstAttachedDevices()) {
+                    auto attachedDevices = module.getFirstAttachedDevices()->getItem();
+                    if (!attachedDevices.empty()) {
+                        mModulesWithDevicesNames.insert(module.getName());
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.h
new file mode 100644
index 0000000..f798839
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/7.0/PolicyConfig.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.
+ */
+
+#pragma once
+
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <utils/Errors.h>
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android_audio_policy_configuration_V7_0.h>
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::CPP_VERSION;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+using Module = Modules::Module;
+}
+
+class PolicyConfig {
+  public:
+    explicit PolicyConfig(const std::string& configFileName)
+        : mConfigFileName{configFileName},
+          mFilePath{findExistingConfigurationFile(mConfigFileName)},
+          mConfig{xsd::read(mFilePath.c_str())} {
+        init();
+    }
+    PolicyConfig(const std::string& configPath, const std::string& configFileName)
+        : mConfigFileName{configFileName},
+          mFilePath{configPath + "/" + mConfigFileName},
+          mConfig{xsd::read(mFilePath.c_str())} {
+        init();
+    }
+    android::status_t getStatus() const { return mStatus; }
+    std::string getError() const;
+    const std::string& getFilePath() const { return mFilePath; }
+    const xsd::Module* getModuleFromName(const std::string& name) const;
+    const xsd::Module* getPrimaryModule() const { return mPrimaryModule; }
+    const std::set<std::string>& getModulesWithDevicesNames() const {
+        return mModulesWithDevicesNames;
+    }
+    std::string getAttachedSinkDeviceForMixPort(const std::string& moduleName,
+                                                const std::string& mixPortName) const {
+        return findAttachedDevice(getAttachedDevices(moduleName),
+                                  getSinkDevicesForMixPort(moduleName, mixPortName));
+    }
+    std::string getAttachedSourceDeviceForMixPort(const std::string& moduleName,
+                                                  const std::string& mixPortName) const {
+        return findAttachedDevice(getAttachedDevices(moduleName),
+                                  getSourceDevicesForMixPort(moduleName, mixPortName));
+    }
+    std::optional<DeviceAddress> getSinkDeviceForMixPort(const std::string& moduleName,
+                                                         const std::string& mixPortName) const;
+    std::optional<DeviceAddress> getSourceDeviceForMixPort(const std::string& moduleName,
+                                                           const std::string& mixPortName) const;
+    bool haveInputProfilesInModule(const std::string& name) const;
+
+  private:
+    static std::string findExistingConfigurationFile(const std::string& fileName);
+    std::string findAttachedDevice(const std::vector<std::string>& attachedDevices,
+                                   const std::set<std::string>& possibleDevices) const;
+    const std::vector<std::string>& getAttachedDevices(const std::string& moduleName) const;
+    std::optional<DeviceAddress> getDeviceAddressOfDevicePort(
+            const std::string& moduleName, const std::string& devicePortName) const;
+    std::string getDevicePortTagNameFromType(const std::string& moduleName,
+                                             const AudioDevice& deviceType) const;
+    std::set<std::string> getSinkDevicesForMixPort(const std::string& moduleName,
+                                                   const std::string& mixPortName) const;
+    std::set<std::string> getSourceDevicesForMixPort(const std::string& moduleName,
+                                                     const std::string& mixPortName) const;
+    void init();
+
+    const std::string mConfigFileName;
+    const std::string mFilePath;
+    std::optional<xsd::AudioPolicyConfiguration> mConfig;
+    android::status_t mStatus = android::NO_INIT;
+    const xsd::Module* mPrimaryModule;
+    std::set<std::string> mModulesWithDevicesNames;
+};
diff --git a/audio/core/all-versions/vts/functional/Android.bp b/audio/core/all-versions/vts/functional/Android.bp
index 729ee7a..9183191 100644
--- a/audio/core/all-versions/vts/functional/Android.bp
+++ b/audio/core/all-versions/vts/functional/Android.bp
@@ -14,14 +14,20 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "VtsHalAudioTargetTest_defaults",
     defaults: ["VtsHalTargetTestDefaults"],
     static_libs: [
         "android.hardware.audio.common.test.utility",
-        "libaudiofoundation",
-        "libaudiopolicycomponents",
-        "libmedia_helper",
         "libxml2",
     ],
     shared_libs: [
@@ -44,6 +50,9 @@
         "2.0/AudioPrimaryHidlHalTest.cpp",
     ],
     static_libs: [
+        "libaudiofoundation",
+        "libaudiopolicycomponents",
+        "libmedia_helper",
         "android.hardware.audio@2.0",
         "android.hardware.audio.common@2.0",
     ],
@@ -55,7 +64,7 @@
     data: [
         ":audio_policy_configuration_V2_0",
     ],
-    // Use test_config for vts-core suite.
+    // Use test_config for vts suite.
     // TODO(b/146104851): Add auto-gen rules and remove it.
     test_config: "VtsHalAudioV2_0TargetTest.xml",
 }
@@ -67,6 +76,9 @@
         "4.0/AudioPrimaryHidlHalTest.cpp",
     ],
     static_libs: [
+        "libaudiofoundation",
+        "libaudiopolicycomponents",
+        "libmedia_helper",
         "android.hardware.audio@4.0",
         "android.hardware.audio.common@4.0",
     ],
@@ -78,7 +90,7 @@
     data: [
         ":audio_policy_configuration_V4_0",
     ],
-    // Use test_config for vts-core suite.
+    // Use test_config for vts suite.
     // TODO(b/146104851): Add auto-gen rules and remove it.
     test_config: "VtsHalAudioV4_0TargetTest.xml",
 }
@@ -90,6 +102,9 @@
         "5.0/AudioPrimaryHidlHalTest.cpp",
     ],
     static_libs: [
+        "libaudiofoundation",
+        "libaudiopolicycomponents",
+        "libmedia_helper",
         "android.hardware.audio@5.0",
         "android.hardware.audio.common@5.0",
     ],
@@ -101,7 +116,7 @@
     data: [
         ":audio_policy_configuration_V5_0",
     ],
-    // Use test_config for vts-core suite.
+    // Use test_config for vts suite.
     // TODO(b/146104851): Add auto-gen rules and remove it.
     test_config: "VtsHalAudioV5_0TargetTest.xml",
 }
@@ -111,8 +126,12 @@
     defaults: ["VtsHalAudioTargetTest_defaults"],
     srcs: [
         "6.0/AudioPrimaryHidlHalTest.cpp",
+        "6.0/Generators.cpp",
     ],
     static_libs: [
+        "libaudiofoundation",
+        "libaudiopolicycomponents",
+        "libmedia_helper",
         "android.hardware.audio@6.0",
         "android.hardware.audio.common@6.0",
     ],
@@ -124,7 +143,100 @@
     data: [
         ":audio_policy_configuration_V6_0",
     ],
-    // Use test_config for vts-core suite.
+    // Use test_config for vts suite.
     // TODO(b/146104851): Add auto-gen rules and remove it.
     test_config: "VtsHalAudioV6_0TargetTest.xml",
 }
+
+cc_test {
+    name: "VtsHalAudioV7_0TargetTest",
+    defaults: ["VtsHalAudioTargetTest_defaults"],
+    srcs: [
+        "7.0/AudioPrimaryHidlHalTest.cpp",
+        "7.0/Generators.cpp",
+        "7.0/PolicyConfig.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",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+    data: [
+        ":audio_policy_configuration_V7_0",
+    ],
+    // Use test_config for vts suite.
+    // TODO(b/146104851): Add auto-gen rules and remove it.
+    test_config: "VtsHalAudioV7_0TargetTest.xml",
+}
+
+// Note: the following aren't VTS tests, but rather unit tests
+// to verify correctness of test utilities.
+cc_test {
+    name: "HalAudioStreamWorkerTest",
+    host_supported: true,
+    srcs: [
+        "tests/streamworker_tests.cpp",
+    ],
+}
+
+cc_test {
+    name: "HalAudioV6_0GeneratorTest",
+    defaults: ["VtsHalAudioTargetTest_defaults"],
+    srcs: [
+        "6.0/Generators.cpp",
+        "tests/generators_tests.cpp",
+    ],
+    static_libs: [
+        "android.hardware.audio@6.0",
+        "android.hardware.audio.common@6.0",
+        "libaudiofoundation",
+        "libaudiopolicycomponents",
+        "libmedia_helper",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+    data: [
+        "tests/apm_config_no_vx.xml",
+        "tests/apm_config_with_vx.xml",
+    ],
+    test_config: "tests/HalAudioV6_0GeneratorTest.xml",
+}
+
+cc_test {
+    name: "HalAudioV7_0GeneratorTest",
+    defaults: ["VtsHalAudioTargetTest_defaults"],
+    srcs: [
+        "7.0/Generators.cpp",
+        "7.0/PolicyConfig.cpp",
+        "tests/generators_tests.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",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+    data: [
+        "tests/apm_config_no_vx_7_0.xml",
+        "tests/apm_config_with_vx_7_0.xml",
+    ],
+    test_config: "tests/HalAudioV7_0GeneratorTest.xml",
+}
diff --git a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
index d5af335..aa7fd8e 100644
--- a/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
+++ b/audio/core/all-versions/vts/functional/AudioPrimaryHidlHalTest.h
@@ -42,8 +42,11 @@
 #include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h)
 #include PATH(android/hardware/audio/FILE_VERSION/types.h)
 #include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <android_audio_policy_configuration_V7_0.h>
+#endif
 
-#include <Serializer.h>
 #include <fmq/EventFlag.h>
 #include <fmq/MessageQueue.h>
 #include <hidl/GtestPrinter.h>
@@ -56,6 +59,7 @@
 #include "utility/ReturnIn.h"
 #include "utility/ValidateXml.h"
 
+#include "AudioTestDefinitions.h"
 /** Provide version specific functions that are used in the generic tests */
 #if MAJOR_VERSION == 2
 #include "2.0/AudioPrimaryHidlHalUtils.h"
@@ -63,14 +67,6 @@
 #include "4.0/AudioPrimaryHidlHalUtils.h"
 #endif
 
-using std::initializer_list;
-using std::list;
-using std::string;
-using std::to_string;
-using std::vector;
-
-using ::android::AudioPolicyConfig;
-using ::android::HwModule;
 using ::android::NO_INIT;
 using ::android::OK;
 using ::android::sp;
@@ -93,18 +89,38 @@
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::common::test::utility;
 using namespace ::android::hardware::audio::CPP_VERSION;
+using ReadParameters = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadParameters;
+using ReadStatus = ::android::hardware::audio::CPP_VERSION::IStreamIn::ReadStatus;
+using WriteCommand = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteCommand;
+using WriteStatus = ::android::hardware::audio::CPP_VERSION::IStreamOut::WriteStatus;
+#if MAJOR_VERSION >= 7
+// Make an alias for enumerations generated from the APM config XSD.
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+}
+#endif
 
 // Typical accepted results from interface methods
 static auto okOrNotSupported = {Result::OK, Result::NOT_SUPPORTED};
 static auto okOrNotSupportedOrInvalidArgs = {Result::OK, Result::NOT_SUPPORTED,
                                              Result::INVALID_ARGUMENTS};
+static auto okOrInvalidState = {Result::OK, Result::INVALID_STATE};
 static auto okOrInvalidStateOrNotSupported = {Result::OK, Result::INVALID_STATE,
                                               Result::NOT_SUPPORTED};
 static auto invalidArgsOrNotSupported = {Result::INVALID_ARGUMENTS, Result::NOT_SUPPORTED};
 static auto invalidStateOrNotSupported = {Result::INVALID_STATE, Result::NOT_SUPPORTED};
 
-#define AUDIO_PRIMARY_HIDL_HAL_TEST
 #include "DeviceManager.h"
+#if MAJOR_VERSION <= 6
+#include "PolicyConfig.h"
+#if MAJOR_VERSION == 6
+#include "6.0/Generators.h"
+#endif
+#elif MAJOR_VERSION >= 7
+#include "7.0/Generators.h"
+#include "7.0/PolicyConfig.h"
+#endif
+#include "StreamWorker.h"
 
 class HidlTest : public ::testing::Test {
   public:
@@ -136,95 +152,40 @@
 ////////////////////////// Audio policy configuration ////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-static constexpr char kConfigFileName[] = "audio_policy_configuration.xml";
-
 // Stringify the argument.
 #define QUOTE(x) #x
 #define STRINGIFY(x) QUOTE(x)
 
-struct PolicyConfigData {
-    android::HwModuleCollection hwModules;
-    android::DeviceVector availableOutputDevices;
-    android::DeviceVector availableInputDevices;
-    sp<android::DeviceDescriptor> defaultOutputDevice;
-};
-
-class PolicyConfig : private PolicyConfigData, public AudioPolicyConfig {
-   public:
-    PolicyConfig()
-        : AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
-                            defaultOutputDevice) {
-        for (const auto& location : android::audio_get_configuration_paths()) {
-            std::string path = location + '/' + kConfigFileName;
-            if (access(path.c_str(), F_OK) == 0) {
-                mFilePath = path;
-                break;
-            }
-        }
-        mStatus = android::deserializeAudioPolicyFile(mFilePath.c_str(), this);
-        if (mStatus == OK) {
-            mPrimaryModule = getHwModules().getModuleFromName(DeviceManager::kPrimaryDevice);
-            // Available devices are not 'attached' to modules at this moment.
-            // Need to go over available devices and find their module.
-            for (const auto& device : availableOutputDevices) {
-                for (const auto& module : hwModules) {
-                    if (module->getDeclaredDevices().indexOf(device) >= 0) {
-                        mModulesWithDevicesNames.insert(module->getName());
-                        break;
-                    }
-                }
-            }
-            for (const auto& device : availableInputDevices) {
-                for (const auto& module : hwModules) {
-                    if (module->getDeclaredDevices().indexOf(device) >= 0) {
-                        mModulesWithDevicesNames.insert(module->getName());
-                        break;
-                    }
-                }
-            }
-        }
-    }
-    status_t getStatus() const { return mStatus; }
-    std::string getError() const {
-        if (mFilePath.empty()) {
-            return std::string{"Could not find "} + kConfigFileName +
-                   " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
-        } else {
-            return "Invalid config file: " + mFilePath;
-        }
-    }
-    const std::string& getFilePath() const { return mFilePath; }
-    sp<const HwModule> getModuleFromName(const std::string& name) const {
-        return getHwModules().getModuleFromName(name.c_str());
-    }
-    sp<const HwModule> getPrimaryModule() const { return mPrimaryModule; }
-    const std::set<std::string>& getModulesWithDevicesNames() const {
-        return mModulesWithDevicesNames;
-    }
-
-   private:
-    status_t mStatus = NO_INIT;
-    std::string mFilePath;
-    sp<HwModule> mPrimaryModule = nullptr;
-    std::set<std::string> mModulesWithDevicesNames;
-};
+static constexpr char kConfigFileName[] = "audio_policy_configuration.xml";
 
 // Cached policy config after parsing for faster test startup
 const PolicyConfig& getCachedPolicyConfig() {
     static std::unique_ptr<PolicyConfig> policyConfig = [] {
-        auto config = std::make_unique<PolicyConfig>();
+        auto config = std::make_unique<PolicyConfig>(kConfigFileName);
         return config;
     }();
     return *policyConfig;
 }
 
+TEST(CheckConfig, audioPolicyConfigurationValidation) {
+    const auto factories = ::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor);
+    if (factories.size() == 0) {
+        GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no factory instances "
+                        "are found.";
+    }
+    RecordProperty("description",
+                   "Verify that the audio policy configuration file "
+                   "is valid according to the schema");
+
+    const char* xsd = "/data/local/tmp/audio_policy_configuration_" STRINGIFY(CPP_VERSION) ".xsd";
+    EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(kConfigFileName,
+                                            android::audio_get_configuration_paths(), xsd);
+}
+
 //////////////////////////////////////////////////////////////////////////////
 //////////////////// Test parameter types and definitions ////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
-enum { PARAM_FACTORY_NAME, PARAM_DEVICE_NAME };
-using DeviceParameter = std::tuple<std::string, std::string>;
-
 static inline std::string DeviceParameterToString(
         const ::testing::TestParamInfo<DeviceParameter>& info) {
     const auto& deviceName = std::get<PARAM_DEVICE_NAME>(info.param);
@@ -293,21 +254,6 @@
     }
 };
 
-TEST(CheckConfig, audioPolicyConfigurationValidation) {
-    auto deviceParameters = getDeviceParametersForFactoryTests();
-    if (deviceParameters.size() == 0) {
-        GTEST_SKIP() << "Skipping audioPolicyConfigurationValidation because no device parameter "
-                        "is found.";
-    }
-    RecordProperty("description",
-                   "Verify that the audio policy configuration file "
-                   "is valid according to the schema");
-
-    const char* xsd = "/data/local/tmp/audio_policy_configuration_" STRINGIFY(CPP_VERSION) ".xsd";
-    EXPECT_ONE_VALID_XML_MULTIPLE_LOCATIONS(kConfigFileName,
-                                            android::audio_get_configuration_paths(), xsd);
-}
-
 class AudioPolicyConfigTest : public AudioHidlTestWithDeviceParameter {
   public:
     void SetUp() override {
@@ -331,6 +277,9 @@
 INSTANTIATE_TEST_CASE_P(AudioHidl, AudioPolicyConfigTest,
                         ::testing::ValuesIn(getDeviceParametersForFactoryTests()),
                         &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPolicyConfigTest);
 
 //////////////////////////////////////////////////////////////////////////////
 ////////////////////// getService audio_devices_factory //////////////////////
@@ -366,6 +315,9 @@
 INSTANTIATE_TEST_CASE_P(AudioHidl, AudioHidlTest,
                         ::testing::ValuesIn(getDeviceParametersForFactoryTests()),
                         &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioHidlTest);
 
 //////////////////////////////////////////////////////////////////////////////
 /////////////////////////////// openDevice ///////////////////////////////////
@@ -391,6 +343,9 @@
 
 INSTANTIATE_TEST_CASE_P(AudioHidlDevice, AudioHidlDeviceTest,
                         ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioHidlDeviceTest);
 
 //////////////////////////////////////////////////////////////////////////////
 /////////////////////////////// openDevice primary ///////////////////////////
@@ -418,6 +373,9 @@
 INSTANTIATE_TEST_CASE_P(AudioPrimaryHidl, AudioPrimaryHidlTest,
                         ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
                         &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPrimaryHidlTest);
 
 //////////////////////////////////////////////////////////////////////////////
 ///////////////////// {set,get}{Master,Mic}{Mute,Volume} /////////////////////
@@ -437,9 +395,10 @@
      *  The getter and/or the setter may return NOT_SUPPORTED if optionality == OPTIONAL.
      */
     template <Optionality optionality = REQUIRED, class IUTGetter, class Getter, class Setter>
-    void testAccessors(IUTGetter iutGetter, const string& propertyName,
-                       const Initial expectedInitial, list<Property> valuesToTest, Setter setter,
-                       Getter getter, const vector<Property>& invalidValues = {}) {
+    void testAccessors(IUTGetter iutGetter, const std::string& propertyName,
+                       const Initial expectedInitial, std::list<Property> valuesToTest,
+                       Setter setter, Getter getter,
+                       const std::vector<Property>& invalidValues = {}) {
         const auto expectedResults = {Result::OK,
                                       optionality == OPTIONAL ? Result::NOT_SUPPORTED : Result::OK};
 
@@ -483,9 +442,9 @@
         EXPECT_RESULT(expectedResults, ((this->*iutGetter)().get()->*setter)(initialValue));
     }
     template <Optionality optionality = REQUIRED, class Getter, class Setter>
-    void testAccessors(const string& propertyName, const Initial expectedInitial,
-                       list<Property> valuesToTest, Setter setter, Getter getter,
-                       const vector<Property>& invalidValues = {}) {
+    void testAccessors(const std::string& propertyName, const Initial expectedInitial,
+                       std::list<Property> valuesToTest, Setter setter, Getter getter,
+                       const std::vector<Property>& invalidValues = {}) {
         testAccessors<optionality>(&BaseTestClass::getDevice, propertyName, expectedInitial,
                                    valuesToTest, setter, getter, invalidValues);
     }
@@ -513,6 +472,10 @@
 INSTANTIATE_TEST_CASE_P(BoolAccessorPrimaryHidl, BoolAccessorPrimaryHidlTest,
                         ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
                         &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BoolAccessorHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BoolAccessorPrimaryHidlTest);
 
 using FloatAccessorHidlTest = AccessorHidlTest<float>;
 TEST_P(FloatAccessorHidlTest, MasterVolumeTest) {
@@ -525,6 +488,9 @@
 
 INSTANTIATE_TEST_CASE_P(FloatAccessorHidl, FloatAccessorHidlTest,
                         ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FloatAccessorHidlTest);
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////////////////////// AudioPatches ////////////////////////////////
@@ -547,22 +513,13 @@
 
 INSTANTIATE_TEST_CASE_P(AudioPatchHidl, AudioPatchHidlTest,
                         ::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
-
-// Nesting a tuple in another tuple allows to use GTest Combine function to generate
-// all combinations of devices and configs.
-enum { PARAM_DEVICE, PARAM_CONFIG, PARAM_FLAGS };
-enum { INDEX_INPUT, INDEX_OUTPUT };
-using DeviceConfigParameter =
-        std::tuple<DeviceParameter, AudioConfig, std::variant<AudioInputFlag, AudioOutputFlag>>;
-
-#if MAJOR_VERSION >= 6
-const std::vector<DeviceConfigParameter>& getInputDeviceConfigParameters();
-const std::vector<DeviceConfigParameter>& getOutputDeviceConfigParameters();
-#endif
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioPatchHidlTest);
 
 #if MAJOR_VERSION >= 4
-static string SanitizeStringForGTestName(const string& s) {
-    string result = s;
+static std::string SanitizeStringForGTestName(const std::string& s) {
+    std::string result = s;
     for (size_t i = 0; i < result.size(); i++) {
         // gtest test names must only contain alphanumeric characters
         if (!std::isalnum(result[i])) result[i] = '_';
@@ -576,43 +533,57 @@
  * As the only parameter changing are channel mask and sample rate,
  * only print those ones in the test name.
  */
-static string DeviceConfigParameterToString(
+static std::string DeviceConfigParameterToString(
         const testing::TestParamInfo<DeviceConfigParameter>& info) {
     const AudioConfig& config = std::get<PARAM_CONFIG>(info.param);
     const auto deviceName = DeviceParameterToString(::testing::TestParamInfo<DeviceParameter>{
             std::get<PARAM_DEVICE>(info.param), info.index});
-    return (deviceName.empty() ? "" : deviceName + "_") + to_string(info.index) + "__" +
-           to_string(config.sampleRateHz) + "_" +
-           // "MONO" is more clear than "FRONT_LEFT"
-           ((config.channelMask == mkEnumBitfield(AudioChannelMask::OUT_MONO) ||
-             config.channelMask == mkEnumBitfield(AudioChannelMask::IN_MONO))
-                    ? "MONO"
+    const auto devicePart =
+            (deviceName.empty() ? "" : deviceName + "_") + std::to_string(info.index);
+    // The types had changed a lot between versions 2, 4..6 and 7. Use separate
+    // code sections for easier understanding.
 #if MAJOR_VERSION == 2
-                    : ::testing::PrintToString(config.channelMask)
-#elif MAJOR_VERSION >= 4
-                    // In V4 and above the channel mask is a bitfield.
-                    // Printing its value using HIDL's toString for a bitfield emits a lot of extra
-                    // text due to overlapping constant values. Instead, we print the bitfield value
-                    // as if it was a single value + its hex representation
-                    : SanitizeStringForGTestName(
-                              ::testing::PrintToString(AudioChannelMask(config.channelMask)) + "_" +
-                              toHexString(config.channelMask))
+    const auto configPart =
+            std::to_string(config.sampleRateHz) + "_" +
+            // "MONO" is more clear than "FRONT_LEFT"
+            (config.channelMask == AudioChannelMask::OUT_MONO ||
+                             config.channelMask == AudioChannelMask::IN_MONO
+                     ? "MONO"
+                     : ::testing::PrintToString(config.channelMask)) +
+            "_" +
+            std::visit([](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
+                       std::get<PARAM_FLAGS>(info.param));
+#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
+    const auto configPart =
+            std::to_string(config.sampleRateHz) + "_" +
+            // "MONO" is more clear than "FRONT_LEFT"
+            (config.channelMask == mkEnumBitfield(AudioChannelMask::OUT_MONO) ||
+                             config.channelMask == mkEnumBitfield(AudioChannelMask::IN_MONO)
+                     ? "MONO"
+                     // In V4 and above the channel mask is a bitfield.
+                     // Printing its value using HIDL's toString for a bitfield emits a lot of extra
+                     // text due to overlapping constant values. Instead, we print the bitfield
+                     // value as if it was a single value + its hex representation
+                     : SanitizeStringForGTestName(
+                               ::testing::PrintToString(AudioChannelMask(config.channelMask)) +
+                               "_" + toHexString(config.channelMask))) +
+            "_" +
+            SanitizeStringForGTestName(std::visit(
+                    [](auto&& arg) -> std::string {
+                        using T = std::decay_t<decltype(arg)>;
+                        // Need to use FQN of toString to avoid confusing the compiler
+                        return ::android::hardware::audio::common::CPP_VERSION::toString<T>(
+                                hidl_bitfield<T>(arg));
+                    },
+                    std::get<PARAM_FLAGS>(info.param)));
+#elif MAJOR_VERSION >= 7
+    const auto configPart =
+            std::to_string(config.base.sampleRateHz) + "_" +
+            // The channel masks and flags are vectors of strings, just need to sanitize them.
+            SanitizeStringForGTestName(::testing::PrintToString(config.base.channelMask)) + "_" +
+            SanitizeStringForGTestName(::testing::PrintToString(std::get<PARAM_FLAGS>(info.param)));
 #endif
-                    ) +
-           "_" +
-#if MAJOR_VERSION == 2
-           std::visit([](auto&& arg) -> std::string { return ::testing::PrintToString(arg); },
-                      std::get<PARAM_FLAGS>(info.param));
-#elif MAJOR_VERSION >= 4
-           SanitizeStringForGTestName(std::visit(
-                   [](auto&& arg) -> std::string {
-                       using T = std::decay_t<decltype(arg)>;
-                       // Need to use FQN of toString to avoid confusing the compiler
-                       return ::android::hardware::audio::common::CPP_VERSION::toString<T>(
-                               hidl_bitfield<T>(arg));
-                   },
-                   std::get<PARAM_FLAGS>(info.param)));
-#endif
+    return devicePart + "__" + configPart;
 }
 
 class AudioHidlTestWithDeviceConfigParameter
@@ -638,7 +609,7 @@
     AudioOutputFlag getOutputFlags() const {
         return std::get<INDEX_OUTPUT>(std::get<PARAM_FLAGS>(GetParam()));
     }
-#elif MAJOR_VERSION >= 4
+#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
     hidl_bitfield<AudioInputFlag> getInputFlags() const {
         return hidl_bitfield<AudioInputFlag>(
                 std::get<INDEX_INPUT>(std::get<PARAM_FLAGS>(GetParam())));
@@ -647,10 +618,17 @@
         return hidl_bitfield<AudioOutputFlag>(
                 std::get<INDEX_OUTPUT>(std::get<PARAM_FLAGS>(GetParam())));
     }
+#elif MAJOR_VERSION >= 7
+    hidl_vec<AudioInOutFlag> getInputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); }
+    hidl_vec<AudioInOutFlag> getOutputFlags() const { return std::get<PARAM_FLAGS>(GetParam()); }
 #endif
 };
 
+#if MAJOR_VERSION <= 6
+#define AUDIO_PRIMARY_HIDL_HAL_TEST
 #include "ConfigHelper.h"
+#undef AUDIO_PRIMARY_HIDL_HAL_TEST
+#endif
 
 //////////////////////////////////////////////////////////////////////////////
 ///////////////////////////// getInputBufferSize /////////////////////////////
@@ -722,6 +700,10 @@
                         ::testing::ValuesIn(getInputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
 #endif
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OptionalInputBufferSizeTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RequiredInputBufferSizeTest);
 
 //////////////////////////////////////////////////////////////////////////////
 /////////////////////////////// setScreenState ///////////////////////////////
@@ -802,6 +784,11 @@
 ////////////////////////// open{Output,Input}Stream //////////////////////////
 //////////////////////////////////////////////////////////////////////////////
 
+static inline AudioIoHandle getNextIoHandle() {
+    static AudioIoHandle lastHandle{};
+    return ++lastHandle;
+}
+
 // This class is also used by some device tests.
 template <class Stream>
 class StreamHelper {
@@ -811,16 +798,13 @@
     template <class Open>
     void open(Open openStream, const AudioConfig& config, Result* res,
               AudioConfig* suggestedConfigPtr) {
-        // FIXME: Open a stream without an IOHandle
-        //        This is not required to be accepted by hal implementations
-        AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
         AudioConfig suggestedConfig{};
         bool retryWithSuggestedConfig = true;
         if (suggestedConfigPtr == nullptr) {
             suggestedConfigPtr = &suggestedConfig;
             retryWithSuggestedConfig = false;
         }
-        ASSERT_OK(openStream(ioHandle, config, returnIn(*res, mStream, *suggestedConfigPtr)));
+        ASSERT_OK(openStream(mIoHandle, config, returnIn(*res, mStream, *suggestedConfigPtr)));
         switch (*res) {
             case Result::OK:
                 ASSERT_TRUE(mStream != nullptr);
@@ -830,7 +814,7 @@
                 ASSERT_TRUE(mStream == nullptr);
                 if (retryWithSuggestedConfig) {
                     AudioConfig suggestedConfigRetry;
-                    ASSERT_OK(openStream(ioHandle, *suggestedConfigPtr,
+                    ASSERT_OK(openStream(mIoHandle, *suggestedConfigPtr,
                                          returnIn(*res, mStream, suggestedConfigRetry)));
                     ASSERT_OK(*res);
                     ASSERT_TRUE(mStream != nullptr);
@@ -858,8 +842,10 @@
 #endif
         }
     }
+    AudioIoHandle getIoHandle() const { return mIoHandle; }
 
   private:
+    const AudioIoHandle mIoHandle = getNextIoHandle();
     sp<Stream>& mStream;
 };
 
@@ -885,7 +871,6 @@
         return res;
     }
 
-  private:
     void TearDown() override {
         if (open) {
             ASSERT_OK(closeStream());
@@ -893,7 +878,7 @@
         AudioHidlTestWithDeviceConfigParameter::TearDown();
     }
 
-   protected:
+  protected:
     AudioConfig audioConfig;
     DeviceAddress address = {};
     sp<Stream> stream;
@@ -903,10 +888,124 @@
 
 ////////////////////////////// openOutputStream //////////////////////////////
 
+class StreamWriter : public StreamWorker<StreamWriter> {
+  public:
+    StreamWriter(IStreamOut* stream, size_t bufferSize)
+        : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
+    ~StreamWriter() {
+        stop();
+        if (mEfGroup) {
+            EventFlag::deleteEventFlag(&mEfGroup);
+        }
+    }
+
+    typedef MessageQueue<WriteCommand, ::android::hardware::kSynchronizedReadWrite> CommandMQ;
+    typedef MessageQueue<uint8_t, ::android::hardware::kSynchronizedReadWrite> DataMQ;
+    typedef MessageQueue<WriteStatus, ::android::hardware::kSynchronizedReadWrite> StatusMQ;
+
+    bool workerInit() {
+        std::unique_ptr<CommandMQ> tempCommandMQ;
+        std::unique_ptr<DataMQ> tempDataMQ;
+        std::unique_ptr<StatusMQ> tempStatusMQ;
+        Result retval;
+        Return<void> ret = mStream->prepareForWriting(
+                1, mBufferSize,
+                [&](Result r, const CommandMQ::Descriptor& commandMQ,
+                    const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ,
+                    const auto& /*halThreadInfo*/) {
+                    retval = r;
+                    if (retval == Result::OK) {
+                        tempCommandMQ.reset(new CommandMQ(commandMQ));
+                        tempDataMQ.reset(new DataMQ(dataMQ));
+                        tempStatusMQ.reset(new StatusMQ(statusMQ));
+                        if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+                            EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+                        }
+                    }
+                });
+        if (!ret.isOk()) {
+            ALOGE("Transport error while calling prepareForWriting: %s", ret.description().c_str());
+            return false;
+        }
+        if (retval != Result::OK) {
+            ALOGE("Error from prepareForWriting: %d", retval);
+            return false;
+        }
+        if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() ||
+            !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {
+            ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for writing");
+            ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+                     "Command message queue for writing is invalid");
+            ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for writing");
+            ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(),
+                     "Data message queue for writing is invalid");
+            ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for writing");
+            ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+                     "Status message queue for writing is invalid");
+            ALOGE_IF(!mEfGroup, "Event flag creation for writing failed");
+            return false;
+        }
+        mCommandMQ = std::move(tempCommandMQ);
+        mDataMQ = std::move(tempDataMQ);
+        mStatusMQ = std::move(tempStatusMQ);
+        return true;
+    }
+
+    bool workerCycle() {
+        WriteCommand cmd = WriteCommand::WRITE;
+        if (!mCommandMQ->write(&cmd)) {
+            ALOGE("command message queue write failed");
+            return false;
+        }
+        const size_t dataSize = std::min(mData.size(), mDataMQ->availableToWrite());
+        bool success = mDataMQ->write(mData.data(), dataSize);
+        ALOGE_IF(!success, "data message queue write failed");
+        mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY));
+
+        uint32_t efState = 0;
+    retry:
+        status_t ret =
+                mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL), &efState);
+        if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)) {
+            WriteStatus writeStatus;
+            writeStatus.retval = Result::NOT_INITIALIZED;
+            if (!mStatusMQ->read(&writeStatus)) {
+                ALOGE("status message read failed");
+                success = false;
+            }
+            if (writeStatus.retval != Result::OK) {
+                ALOGE("bad write status: %d", writeStatus.retval);
+                success = false;
+            }
+        }
+        if (ret == -EAGAIN || ret == -EINTR) {
+            // Spurious wakeup. This normally retries no more than once.
+            goto retry;
+        } else if (ret) {
+            ALOGE("bad wait status: %d", ret);
+            success = false;
+        }
+        return success;
+    }
+
+  private:
+    IStreamOut* const mStream;
+    const size_t mBufferSize;
+    std::vector<uint8_t> mData;
+    std::unique_ptr<CommandMQ> mCommandMQ;
+    std::unique_ptr<DataMQ> mDataMQ;
+    std::unique_ptr<StatusMQ> mStatusMQ;
+    EventFlag* mEfGroup = nullptr;
+};
+
 class OutputStreamTest : public OpenStreamTest<IStreamOut> {
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp());  // setup base
+#if MAJOR_VERSION <= 6
         address.device = AudioDevice::OUT_DEFAULT;
+#elif MAJOR_VERSION >= 7
+        address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_DEFAULT);
+#endif
         const AudioConfig& config = getConfig();
         auto flags = getOutputFlags();
         testOpen(
@@ -920,13 +1019,21 @@
                 },
                 config);
     }
-#if MAJOR_VERSION >= 4
+#if MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
 
-   protected:
+  protected:
     const SourceMetadata initMetadata = {
         { { AudioUsage::MEDIA,
             AudioContentType::MUSIC,
             1 /* gain */ } }};
+#elif MAJOR_VERSION >= 7
+  protected:
+    const SourceMetadata initMetadata = {
+            { { toString(xsd::AudioUsage::AUDIO_USAGE_MEDIA),
+                toString(xsd::AudioContentType::AUDIO_CONTENT_TYPE_MUSIC),
+                1 /* gain */,
+                toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO),
+                {} } }};
 #endif
 };
 TEST_P(OutputStreamTest, OpenOutputStreamTest) {
@@ -960,13 +1067,145 @@
                         ::testing::ValuesIn(getOutputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
 #endif
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OutputStreamTest);
 
 ////////////////////////////// openInputStream //////////////////////////////
 
+class StreamReader : public StreamWorker<StreamReader> {
+  public:
+    StreamReader(IStreamIn* stream, size_t bufferSize)
+        : mStream(stream), mBufferSize(bufferSize), mData(mBufferSize) {}
+    ~StreamReader() {
+        stop();
+        if (mEfGroup) {
+            EventFlag::deleteEventFlag(&mEfGroup);
+        }
+    }
+
+    typedef MessageQueue<ReadParameters, ::android::hardware::kSynchronizedReadWrite> CommandMQ;
+    typedef MessageQueue<uint8_t, ::android::hardware::kSynchronizedReadWrite> DataMQ;
+    typedef MessageQueue<ReadStatus, ::android::hardware::kSynchronizedReadWrite> StatusMQ;
+
+    bool workerInit() {
+        std::unique_ptr<CommandMQ> tempCommandMQ;
+        std::unique_ptr<DataMQ> tempDataMQ;
+        std::unique_ptr<StatusMQ> tempStatusMQ;
+        Result retval;
+        Return<void> ret = mStream->prepareForReading(
+                1, mBufferSize,
+                [&](Result r, const CommandMQ::Descriptor& commandMQ,
+                    const DataMQ::Descriptor& dataMQ, const StatusMQ::Descriptor& statusMQ,
+                    const auto& /*halThreadInfo*/) {
+                    retval = r;
+                    if (retval == Result::OK) {
+                        tempCommandMQ.reset(new CommandMQ(commandMQ));
+                        tempDataMQ.reset(new DataMQ(dataMQ));
+                        tempStatusMQ.reset(new StatusMQ(statusMQ));
+                        if (tempDataMQ->isValid() && tempDataMQ->getEventFlagWord()) {
+                            EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
+                        }
+                    }
+                });
+        if (!ret.isOk()) {
+            ALOGE("Transport error while calling prepareForReading: %s", ret.description().c_str());
+            return false;
+        }
+        if (retval != Result::OK) {
+            ALOGE("Error from prepareForReading: %d", retval);
+            return false;
+        }
+        if (!tempCommandMQ || !tempCommandMQ->isValid() || !tempDataMQ || !tempDataMQ->isValid() ||
+            !tempStatusMQ || !tempStatusMQ->isValid() || !mEfGroup) {
+            ALOGE_IF(!tempCommandMQ, "Failed to obtain command message queue for reading");
+            ALOGE_IF(tempCommandMQ && !tempCommandMQ->isValid(),
+                     "Command message queue for reading is invalid");
+            ALOGE_IF(!tempDataMQ, "Failed to obtain data message queue for reading");
+            ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(),
+                     "Data message queue for reading is invalid");
+            ALOGE_IF(!tempStatusMQ, "Failed to obtain status message queue for reading");
+            ALOGE_IF(tempStatusMQ && !tempStatusMQ->isValid(),
+                     "Status message queue for reading is invalid");
+            ALOGE_IF(!mEfGroup, "Event flag creation for reading failed");
+            return false;
+        }
+        mCommandMQ = std::move(tempCommandMQ);
+        mDataMQ = std::move(tempDataMQ);
+        mStatusMQ = std::move(tempStatusMQ);
+        return true;
+    }
+
+    bool workerCycle() {
+        ReadParameters params;
+        params.command = IStreamIn::ReadCommand::READ;
+        params.params.read = mBufferSize;
+        if (!mCommandMQ->write(&params)) {
+            ALOGE("command message queue write failed");
+            return false;
+        }
+        mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
+
+        uint32_t efState = 0;
+        bool success = true;
+    retry:
+        status_t ret =
+                mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState);
+        if (efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)) {
+            ReadStatus readStatus;
+            readStatus.retval = Result::NOT_INITIALIZED;
+            if (!mStatusMQ->read(&readStatus)) {
+                ALOGE("status message read failed");
+                success = false;
+            }
+            if (readStatus.retval != Result::OK) {
+                ALOGE("bad read status: %d", readStatus.retval);
+                success = false;
+            }
+            const size_t dataSize = std::min(mData.size(), mDataMQ->availableToRead());
+            if (!mDataMQ->read(mData.data(), dataSize)) {
+                ALOGE("data message queue read failed");
+                success = false;
+            }
+        }
+        if (ret == -EAGAIN || ret == -EINTR) {
+            // Spurious wakeup. This normally retries no more than once.
+            goto retry;
+        } else if (ret) {
+            ALOGE("bad wait status: %d", ret);
+            success = false;
+        }
+        return success;
+    }
+
+  private:
+    IStreamIn* const mStream;
+    const size_t mBufferSize;
+    std::vector<uint8_t> mData;
+    std::unique_ptr<CommandMQ> mCommandMQ;
+    std::unique_ptr<DataMQ> mDataMQ;
+    std::unique_ptr<StatusMQ> mStatusMQ;
+    EventFlag* mEfGroup = nullptr;
+};
+
 class InputStreamTest : public OpenStreamTest<IStreamIn> {
     void SetUp() override {
         ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp());  // setup base
+#if MAJOR_VERSION <= 6
         address.device = AudioDevice::IN_DEFAULT;
+#elif MAJOR_VERSION >= 7
+        auto maybeSourceAddress = getCachedPolicyConfig().getSourceDeviceForMixPort(
+                getDeviceName(), getMixPortName());
+        if (maybeSourceAddress.has_value() &&
+            !xsd::isTelephonyDevice(maybeSourceAddress.value().deviceType)) {
+            address = maybeSourceAddress.value();
+            auto& metadata = initMetadata.tracks[0];
+            metadata.source = toString(xsd::AudioSource::AUDIO_SOURCE_UNPROCESSED);
+            metadata.channelMask = getConfig().base.channelMask;
+        } else {
+            address.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_DEFAULT);
+        }
+#endif
         const AudioConfig& config = getConfig();
         auto flags = getInputFlags();
         testOpen(
@@ -979,9 +1218,16 @@
 
    protected:
 #if MAJOR_VERSION == 2
-    const AudioSource initMetadata = AudioSource::DEFAULT;
-#elif MAJOR_VERSION >= 4
-    const SinkMetadata initMetadata = {{{.source = AudioSource::DEFAULT, .gain = 1}}};
+     const AudioSource initMetadata = AudioSource::DEFAULT;
+#elif MAJOR_VERSION >= 4 && MAJOR_VERSION <= 6
+     const SinkMetadata initMetadata = {{ {.source = AudioSource::DEFAULT, .gain = 1 } }};
+#elif MAJOR_VERSION >= 7
+     const std::string& getMixPortName() const { return std::get<PARAM_PORT_NAME>(GetParam()); }
+     SinkMetadata initMetadata = {
+             {{.source = toString(xsd::AudioSource::AUDIO_SOURCE_DEFAULT),
+               .gain = 1,
+               .tags = {},
+               .channelMask = toString(xsd::AudioChannelMask::AUDIO_CHANNEL_IN_MONO)}}};
 #endif
 };
 
@@ -1015,6 +1261,9 @@
                         ::testing::ValuesIn(getInputDeviceConfigParameters()),
                         &DeviceConfigParameterToString);
 #endif
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(InputStreamTest);
 
 //////////////////////////////////////////////////////////////////////////////
 ////////////////////////////// IStream getters ///////////////////////////////
@@ -1035,6 +1284,7 @@
 TEST_IO_STREAM(GetFrameCount, "Check that getting stream frame count does not crash the HAL.",
                ASSERT_TRUE(stream->getFrameCount().isOk()))
 
+#if MAJOR_VERSION <= 6
 TEST_IO_STREAM(GetSampleRate, "Check that the stream sample rate == the one it was opened with",
                ASSERT_EQ(audioConfig.sampleRateHz, extract(stream->getSampleRate())))
 
@@ -1043,6 +1293,7 @@
 
 TEST_IO_STREAM(GetFormat, "Check that the stream format == the one it was opened with",
                ASSERT_EQ(audioConfig.format, extract(stream->getFormat())))
+#endif
 
 // TODO: for now only check that the framesize is not incoherent
 TEST_IO_STREAM(GetFrameSize, "Check that the stream frame size == the one it was opened with",
@@ -1052,7 +1303,7 @@
                ASSERT_GE(extract(stream->getBufferSize()), extract(stream->getFrameSize())));
 
 template <class Property, class CapabilityGetter>
-static void testCapabilityGetter(const string& name, IStream* stream,
+static void testCapabilityGetter(const std::string& name, IStream* stream,
                                  CapabilityGetter capabilityGetter,
                                  Return<Property> (IStream::*getter)(),
                                  Return<Result> (IStream::*setter)(Property),
@@ -1088,6 +1339,7 @@
     }
 }
 
+#if MAJOR_VERSION <= 6
 TEST_IO_STREAM(SupportedSampleRate, "Check that the stream sample rate is declared as supported",
                testCapabilityGetter("getSupportedSampleRate", stream.get(),
                                     &GetSupported::sampleRates, &IStream::getSampleRate,
@@ -1105,19 +1357,74 @@
 TEST_IO_STREAM(SupportedFormat, "Check that the stream format is declared as supported",
                testCapabilityGetter("getSupportedFormat", stream.get(), &GetSupported::formats,
                                     &IStream::getFormat, &IStream::setFormat))
+#else
+static void testGetSupportedProfiles(IStream* stream) {
+    Result res;
+    hidl_vec<AudioProfile> profiles;
+    auto ret = stream->getSupportedProfiles(returnIn(res, profiles));
+    EXPECT_TRUE(ret.isOk());
+    if (res == Result::OK) {
+        EXPECT_GT(profiles.size(), 0);
+    } else {
+        EXPECT_EQ(Result::NOT_SUPPORTED, res);
+    }
+}
+
+TEST_IO_STREAM(GetSupportedProfiles, "Try to call optional method GetSupportedProfiles",
+               testGetSupportedProfiles(stream.get()))
+
+static void testSetAudioProperties(IStream* stream) {
+    Result res;
+    hidl_vec<AudioProfile> profiles;
+    auto ret = stream->getSupportedProfiles(returnIn(res, profiles));
+    EXPECT_TRUE(ret.isOk());
+    if (res == Result::NOT_SUPPORTED) {
+        GTEST_SKIP() << "Retrieving supported profiles is not implemented";
+    }
+    for (const auto& profile : profiles) {
+        for (const auto& sampleRate : profile.sampleRates) {
+            for (const auto& channelMask : profile.channelMasks) {
+                AudioConfigBaseOptional config;
+                config.format.value(profile.format);
+                config.sampleRateHz.value(sampleRate);
+                config.channelMask.value(channelMask);
+                auto ret = stream->setAudioProperties(config);
+                EXPECT_TRUE(ret.isOk());
+                EXPECT_EQ(Result::OK, ret)
+                        << profile.format << "; " << sampleRate << "; " << channelMask;
+            }
+        }
+    }
+}
+
+TEST_IO_STREAM(SetAudioProperties, "Call setAudioProperties for all supported profiles",
+               testSetAudioProperties(stream.get()))
+#endif  // MAJOR_VERSION <= 6
 
 static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
+#if MAJOR_VERSION <= 6
     uint32_t sampleRateHz;
     auto mask = mkEnumBitfield<AudioChannelMask>({});
     AudioFormat format;
 
-    stream->getAudioProperties(returnIn(sampleRateHz, mask, format));
+    auto ret = stream->getAudioProperties(returnIn(sampleRateHz, mask, format));
+    EXPECT_TRUE(ret.isOk());
 
     // FIXME: the qcom hal it does not currently negotiate the sampleRate &
     // channel mask
     EXPECT_EQ(expectedConfig.sampleRateHz, sampleRateHz);
     EXPECT_EQ(expectedConfig.channelMask, mask);
     EXPECT_EQ(expectedConfig.format, format);
+#elif MAJOR_VERSION >= 7
+    Result res;
+    AudioConfigBase actualConfig{};
+    auto ret = stream->getAudioProperties(returnIn(res, actualConfig));
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::OK, res);
+    EXPECT_EQ(expectedConfig.base.sampleRateHz, actualConfig.sampleRateHz);
+    EXPECT_EQ(expectedConfig.base.channelMask, actualConfig.channelMask);
+    EXPECT_EQ(expectedConfig.base.format, actualConfig.format);
+#endif
 }
 
 TEST_IO_STREAM(GetAudioProperties,
@@ -1128,7 +1435,7 @@
                ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, stream->setHwAvSync(666)))
 
 static void checkGetNoParameter(IStream* stream, hidl_vec<hidl_string> keys,
-                                initializer_list<Result> expectedResults) {
+                                std::initializer_list<Result> expectedResults) {
     hidl_vec<ParameterValue> parameters;
     Result res;
     ASSERT_OK(Parameters::get(stream, keys, returnIn(res, parameters)));
@@ -1239,7 +1546,11 @@
         return;
     }
     ASSERT_OK(res);
+#if MAJOR_VERSION <= 6
     ASSERT_EQ(AudioSource::DEFAULT, source);
+#elif MAJOR_VERSION >= 7
+    ASSERT_EQ(xsd::AudioSource::AUDIO_SOURCE_DEFAULT, xsd::stringToAudioSource(source));
+#endif
 }
 
 static void testUnitaryGain(std::function<Return<Result>(float)> setGain) {
@@ -1254,7 +1565,7 @@
 }
 
 static void testOptionalUnitaryGain(std::function<Return<Result>(float)> setGain,
-                                    string debugName) {
+                                    std::string debugName) {
     auto result = setGain(1);
     ASSERT_IS_OK(result);
     if (result == Result::NOT_SUPPORTED) {
@@ -1274,7 +1585,7 @@
     Result res;
     // Ignore output parameters as the call should fail
     ASSERT_OK(stream->prepareForReading(frameSize, framesCount,
-                                        [&res](auto r, auto&, auto&, auto&, auto&) { res = r; }));
+                                        [&res](auto r, auto&, auto&, auto&, auto) { res = r; }));
     EXPECT_RESULT(Result::INVALID_ARGUMENTS, res);
 }
 
@@ -1311,6 +1622,12 @@
     uint64_t frames;
     uint64_t time;
     ASSERT_OK(stream->getCapturePosition(returnIn(res, frames, time)));
+    // Although 'getCapturePosition' is mandatory in V7, legacy implementations
+    // may return -ENOSYS (which is translated to NOT_SUPPORTED) in cases when
+    // the capture position can't be retrieved, e.g. when the stream isn't
+    // running. Because of this, we don't fail when getting NOT_SUPPORTED
+    // in this test. Behavior of 'getCapturePosition' for running streams is
+    // tested in 'PcmOnlyConfigInputStreamTest' for V7.
     ASSERT_RESULT(okOrInvalidStateOrNotSupported, res);
     if (res == Result::OK) {
         ASSERT_EQ(0U, frames);
@@ -1339,7 +1656,7 @@
     Result res;
     // Ignore output parameters as the call should fail
     ASSERT_OK(stream->prepareForWriting(frameSize, framesCount,
-                                        [&res](auto r, auto&, auto&, auto&, auto&) { res = r; }));
+                                        [&res](auto r, auto&, auto&, auto&, auto) { res = r; }));
     EXPECT_RESULT(Result::INVALID_ARGUMENTS, res);
 }
 
@@ -1494,15 +1811,19 @@
         "If supported, a stream should always succeed to retrieve the "
         "presentation position");
     uint64_t frames;
-    TimeSpec mesureTS;
-    ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, mesureTS)));
+    TimeSpec measureTS;
+    ASSERT_OK(stream->getPresentationPosition(returnIn(res, frames, measureTS)));
+#if MAJOR_VERSION <= 6
     if (res == Result::NOT_SUPPORTED) {
-        doc::partialTest("getpresentationPosition is not supported");
+        doc::partialTest("getPresentationPosition is not supported");
         return;
     }
+#else
+    ASSERT_NE(Result::NOT_SUPPORTED, res) << "getPresentationPosition is mandatory in V7";
+#endif
     ASSERT_EQ(0U, frames);
 
-    if (mesureTS.tvNSec == 0 && mesureTS.tvSec == 0) {
+    if (measureTS.tvNSec == 0 && measureTS.tvSec == 0) {
         // As the stream has never written a frame yet,
         // the timestamp does not really have a meaning, allow to return 0
         return;
@@ -1514,8 +1835,8 @@
 
     auto toMicroSec = [](uint64_t sec, auto nsec) { return sec * 1e+6 + nsec / 1e+3; };
     auto currentTime = toMicroSec(currentTS.tv_sec, currentTS.tv_nsec);
-    auto mesureTime = toMicroSec(mesureTS.tvSec, mesureTS.tvNSec);
-    ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, mesureTime);
+    auto measureTime = toMicroSec(measureTS.tvSec, measureTS.tvNSec);
+    ASSERT_PRED2([](auto c, auto m) { return c - m < 1e+6; }, currentTime, measureTime);
 }
 
 //////////////////////////////////////////////////////////////////////////////
@@ -1553,6 +1874,9 @@
 INSTANTIATE_TEST_CASE_P(TtyModeAccessorPrimaryHidl, TtyModeAccessorPrimaryHidlTest,
                         ::testing::ValuesIn(getDeviceParametersForPrimaryDeviceTests()),
                         &DeviceParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TtyModeAccessorPrimaryHidlTest);
 
 TEST_P(BoolAccessorPrimaryHidlTest, setGetHac) {
     doc::test("Query and set the HAC state");
diff --git a/audio/core/all-versions/vts/functional/AudioTestDefinitions.h b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h
new file mode 100644
index 0000000..aa67630
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/AudioTestDefinitions.h
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <tuple>
+#include <variant>
+#include <vector>
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+enum { PARAM_FACTORY_NAME, PARAM_DEVICE_NAME };
+using DeviceParameter = std::tuple<std::string, std::string>;
+
+// Nesting a tuple in another tuple allows to use GTest Combine function to generate
+// all combinations of devices and configs.
+#if MAJOR_VERSION <= 6
+enum { PARAM_DEVICE, PARAM_CONFIG, PARAM_FLAGS };
+enum { INDEX_INPUT, INDEX_OUTPUT };
+using DeviceConfigParameter =
+        std::tuple<DeviceParameter, android::hardware::audio::common::CPP_VERSION::AudioConfig,
+                   std::variant<android::hardware::audio::common::CPP_VERSION::AudioInputFlag,
+                                android::hardware::audio::common::CPP_VERSION::AudioOutputFlag>>;
+#elif MAJOR_VERSION >= 7
+enum { PARAM_DEVICE, PARAM_PORT_NAME, PARAM_CONFIG, PARAM_FLAGS };
+using DeviceConfigParameter =
+        std::tuple<DeviceParameter, std::string,
+                   android::hardware::audio::common::CPP_VERSION::AudioConfig,
+                   std::vector<android::hardware::audio::CPP_VERSION::AudioInOutFlag>>;
+#endif
diff --git a/audio/core/all-versions/vts/functional/ConfigHelper.h b/audio/core/all-versions/vts/functional/ConfigHelper.h
index 8ef2b43..a2bb1ee 100644
--- a/audio/core/all-versions/vts/functional/ConfigHelper.h
+++ b/audio/core/all-versions/vts/functional/ConfigHelper.h
@@ -14,10 +14,23 @@
  * limitations under the License.
  */
 
-// Code in this file uses 'getCachedPolicyConfig'
-#ifndef AUDIO_PRIMARY_HIDL_HAL_TEST
-#error Must be included from AudioPrimaryHidlTest.h
-#endif
+#pragma once
+
+#include <common/all-versions/VersionUtils.h>
+
+#include "PolicyConfig.h"
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+using ::android::hardware::audio::common::utils::EnumBitfield;
+using ::android::hardware::audio::common::utils::mkEnumBitfield;
+
+// Forward declaration for functions that are substituted
+// in generator unit tests.
+const PolicyConfig& getCachedPolicyConfig();
 
 //////////////////////////////////////////////////////////////////////////////
 //////////////// Required and recommended audio format support ///////////////
@@ -33,7 +46,7 @@
     // FIXME: in the next audio HAL version, test all available devices
     static bool primaryHasMic() {
         auto& policyConfig = getCachedPolicyConfig();
-        if (policyConfig.getStatus() != OK || policyConfig.getPrimaryModule() == nullptr) {
+        if (policyConfig.getStatus() != android::OK || policyConfig.getPrimaryModule() == nullptr) {
             return true;  // Could not get the information, run all tests
         }
         auto getMic = [](auto& devs) {
@@ -46,32 +59,32 @@
     }
 
     // Cache result ?
-    static const vector<AudioConfig> getRequiredSupportPlaybackAudioConfig() {
+    static const std::vector<AudioConfig> getRequiredSupportPlaybackAudioConfig() {
         return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
                                   {8000, 11025, 16000, 22050, 32000, 44100},
                                   {AudioFormat::PCM_16_BIT});
     }
 
-    static const vector<AudioConfig> getRecommendedSupportPlaybackAudioConfig() {
+    static const std::vector<AudioConfig> getRecommendedSupportPlaybackAudioConfig() {
         return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
                                   {24000, 48000}, {AudioFormat::PCM_16_BIT});
     }
 
-    static const vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
+    static const std::vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
         if (!primaryHasMic()) return {};
         return combineAudioConfig({AudioChannelMask::IN_MONO}, {8000, 11025, 16000, 44100},
                                   {AudioFormat::PCM_16_BIT});
     }
-    static const vector<AudioConfig> getRecommendedSupportCaptureAudioConfig() {
+    static const std::vector<AudioConfig> getRecommendedSupportCaptureAudioConfig() {
         if (!primaryHasMic()) return {};
         return combineAudioConfig({AudioChannelMask::IN_STEREO}, {22050, 48000},
                                   {AudioFormat::PCM_16_BIT});
     }
 
-    static vector<AudioConfig> combineAudioConfig(vector<audio_channel_mask_t> channelMasks,
-                                                  vector<uint32_t> sampleRates,
-                                                  audio_format_t format) {
-        vector<AudioConfig> configs;
+    static std::vector<AudioConfig> combineAudioConfig(
+            std::vector<audio_channel_mask_t> channelMasks, std::vector<uint32_t> sampleRates,
+            audio_format_t format) {
+        std::vector<AudioConfig> configs;
         configs.reserve(channelMasks.size() * sampleRates.size());
         for (auto channelMask : channelMasks) {
             for (auto sampleRate : sampleRates) {
@@ -86,10 +99,10 @@
         return configs;
     }
 
-    static vector<AudioConfig> combineAudioConfig(vector<AudioChannelMask> channelMasks,
-                                                  vector<uint32_t> sampleRates,
-                                                  vector<AudioFormat> formats) {
-        vector<AudioConfig> configs;
+    static std::vector<AudioConfig> combineAudioConfig(std::vector<AudioChannelMask> channelMasks,
+                                                       std::vector<uint32_t> sampleRates,
+                                                       std::vector<AudioFormat> formats) {
+        std::vector<AudioConfig> configs;
         configs.reserve(channelMasks.size() * sampleRates.size() * formats.size());
         for (auto channelMask : channelMasks) {
             for (auto sampleRate : sampleRates) {
diff --git a/audio/core/all-versions/vts/functional/DeviceManager.h b/audio/core/all-versions/vts/functional/DeviceManager.h
index 0c0727f..6db78a7 100644
--- a/audio/core/all-versions/vts/functional/DeviceManager.h
+++ b/audio/core/all-versions/vts/functional/DeviceManager.h
@@ -14,10 +14,29 @@
  * limitations under the License.
  */
 
-// Code in this file uses 'environment'
-#ifndef AUDIO_PRIMARY_HIDL_HAL_TEST
-#error Must be included from AudioPrimaryHidlTest.h
-#endif
+#pragma once
+
+#include <unistd.h>
+
+#include <map>
+
+#include <android-base/logging.h>
+#include <hwbinder/IPCThreadState.h>
+
+// clang-format off
+#include PATH(android/hardware/audio/FILE_VERSION/IDevice.h)
+#include PATH(android/hardware/audio/FILE_VERSION/IDevicesFactory.h)
+#include PATH(android/hardware/audio/FILE_VERSION/IPrimaryDevice.h)
+#include PATH(android/hardware/audio/FILE_VERSION/types.h)
+#include PATH(android/hardware/audio/common/FILE_VERSION/types.h)
+// clang-format on
+
+#include "utility/ReturnIn.h"
+
+using ::android::sp;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::common::test::utility;
+using namespace ::android::hardware::audio::CPP_VERSION;
 
 template <class Derived, class Key, class Interface>
 class InterfaceManager {
@@ -55,7 +74,7 @@
         //        the remote device has the time to be destroyed.
         //        flushCommand makes sure all local command are sent, thus should reduce
         //        the latency between local and remote destruction.
-        IPCThreadState::self()->flushCommands();
+        ::android::hardware::IPCThreadState::self()->flushCommands();
         usleep(100 * 1000);
     }
 
diff --git a/audio/core/all-versions/vts/functional/PolicyConfig.h b/audio/core/all-versions/vts/functional/PolicyConfig.h
new file mode 100644
index 0000000..a94041c
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/PolicyConfig.h
@@ -0,0 +1,115 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <set>
+#include <string>
+
+#include <DeviceDescriptor.h>
+#include <HwModule.h>
+#include <Serializer.h>
+#include <gtest/gtest.h>
+#include <system/audio_config.h>
+
+#include "DeviceManager.h"
+
+using ::android::sp;
+using ::android::status_t;
+
+struct PolicyConfigData {
+    android::HwModuleCollection hwModules;
+    android::DeviceVector availableOutputDevices;
+    android::DeviceVector availableInputDevices;
+    sp<android::DeviceDescriptor> defaultOutputDevice;
+};
+
+class PolicyConfig : private PolicyConfigData, public android::AudioPolicyConfig {
+  public:
+    explicit PolicyConfig(const std::string& configFileName)
+        : android::AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
+                                     defaultOutputDevice),
+          mConfigFileName{configFileName} {
+        for (const auto& location : android::audio_get_configuration_paths()) {
+            std::string path = location + '/' + mConfigFileName;
+            if (access(path.c_str(), F_OK) == 0) {
+                mFilePath = path;
+                break;
+            }
+        }
+        init();
+    }
+    PolicyConfig(const std::string& configPath, const std::string& configFileName)
+        : android::AudioPolicyConfig(hwModules, availableOutputDevices, availableInputDevices,
+                                     defaultOutputDevice),
+          mConfigFileName{configFileName},
+          mFilePath{configPath + "/" + mConfigFileName} {
+        init();
+    }
+    status_t getStatus() const { return mStatus; }
+    std::string getError() const {
+        if (mFilePath.empty()) {
+            return std::string{"Could not find "} + mConfigFileName +
+                   " file in: " + testing::PrintToString(android::audio_get_configuration_paths());
+        } else {
+            return "Invalid config file: " + mFilePath;
+        }
+    }
+    const std::string& getFilePath() const { return mFilePath; }
+    sp<const android::HwModule> getModuleFromName(const std::string& name) const {
+        return getHwModules().getModuleFromName(name.c_str());
+    }
+    sp<const android::HwModule> getPrimaryModule() const { return mPrimaryModule; }
+    const std::set<std::string>& getModulesWithDevicesNames() const {
+        return mModulesWithDevicesNames;
+    }
+    bool haveInputProfilesInModule(const std::string& name) const {
+        auto module = getModuleFromName(name);
+        return module && !module->getInputProfiles().empty();
+    }
+
+  private:
+    void init() {
+        mStatus = android::deserializeAudioPolicyFileForVts(mFilePath.c_str(), this);
+        if (mStatus == android::OK) {
+            mPrimaryModule = getModuleFromName(DeviceManager::kPrimaryDevice);
+            // Available devices are not 'attached' to modules at this moment.
+            // Need to go over available devices and find their module.
+            for (const auto& device : availableOutputDevices) {
+                for (const auto& module : hwModules) {
+                    if (module->getDeclaredDevices().indexOf(device) >= 0) {
+                        mModulesWithDevicesNames.insert(module->getName());
+                        break;
+                    }
+                }
+            }
+            for (const auto& device : availableInputDevices) {
+                for (const auto& module : hwModules) {
+                    if (module->getDeclaredDevices().indexOf(device) >= 0) {
+                        mModulesWithDevicesNames.insert(module->getName());
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    const std::string mConfigFileName;
+    status_t mStatus = android::NO_INIT;
+    std::string mFilePath;
+    sp<const android::HwModule> mPrimaryModule = nullptr;
+    std::set<std::string> mModulesWithDevicesNames;
+};
diff --git a/audio/core/all-versions/vts/functional/StreamWorker.h b/audio/core/all-versions/vts/functional/StreamWorker.h
new file mode 100644
index 0000000..68a8024
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/StreamWorker.h
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <sched.h>
+
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+template <typename Impl>
+class StreamWorker {
+    enum class WorkerState { STOPPED, RUNNING, PAUSE_REQUESTED, PAUSED, RESUME_REQUESTED, ERROR };
+
+  public:
+    StreamWorker() = default;
+    ~StreamWorker() { stop(); }
+    bool start() {
+        mWorker = std::thread(&StreamWorker::workerThread, this);
+        std::unique_lock<std::mutex> lock(mWorkerLock);
+        mWorkerCv.wait(lock, [&] { return mWorkerState != WorkerState::STOPPED; });
+        return mWorkerState == WorkerState::RUNNING;
+    }
+    void pause() { switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED); }
+    void resume() { switchWorkerStateSync(WorkerState::PAUSED, WorkerState::RESUME_REQUESTED); }
+    bool hasError() {
+        std::lock_guard<std::mutex> lock(mWorkerLock);
+        return mWorkerState == WorkerState::ERROR;
+    }
+    void stop() {
+        {
+            std::lock_guard<std::mutex> lock(mWorkerLock);
+            if (mWorkerState == WorkerState::STOPPED) return;
+            mWorkerState = WorkerState::STOPPED;
+        }
+        if (mWorker.joinable()) {
+            mWorker.join();
+        }
+    }
+    bool waitForAtLeastOneCycle() {
+        WorkerState newState;
+        switchWorkerStateSync(WorkerState::RUNNING, WorkerState::PAUSE_REQUESTED, &newState);
+        if (newState != WorkerState::PAUSED) return false;
+        switchWorkerStateSync(newState, WorkerState::RESUME_REQUESTED, &newState);
+        return newState == WorkerState::RUNNING;
+    }
+
+    // Methods that need to be provided by subclasses:
+    //
+    // Called once at the beginning of the thread loop. Must return
+    // 'true' to enter the thread loop, otherwise the thread loop
+    // exits and the worker switches into the 'error' state.
+    // bool workerInit();
+    //
+    // Called for each thread loop unless the thread is in 'paused' state.
+    // Must return 'true' to continue running, otherwise the thread loop
+    // exits and the worker switches into the 'error' state.
+    // bool workerCycle();
+
+  private:
+    void switchWorkerStateSync(WorkerState oldState, WorkerState newState,
+                               WorkerState* finalState = nullptr) {
+        std::unique_lock<std::mutex> lock(mWorkerLock);
+        if (mWorkerState != oldState) {
+            if (finalState) *finalState = mWorkerState;
+            return;
+        }
+        mWorkerState = newState;
+        mWorkerCv.wait(lock, [&] { return mWorkerState != newState; });
+        if (finalState) *finalState = mWorkerState;
+    }
+    void workerThread() {
+        bool success = static_cast<Impl*>(this)->workerInit();
+        {
+            std::lock_guard<std::mutex> lock(mWorkerLock);
+            mWorkerState = success ? WorkerState::RUNNING : WorkerState::ERROR;
+        }
+        mWorkerCv.notify_one();
+        if (!success) return;
+
+        for (WorkerState state = WorkerState::RUNNING; state != WorkerState::STOPPED;) {
+            bool needToNotify = false;
+            if (state != WorkerState::PAUSED ? static_cast<Impl*>(this)->workerCycle()
+                                             : (sched_yield(), true)) {
+                //
+                // Pause and resume are synchronous. One worker cycle must complete
+                // before the worker indicates a state change. This is how 'mWorkerState' and
+                // 'state' interact:
+                //
+                // mWorkerState == RUNNING
+                // client sets mWorkerState := PAUSE_REQUESTED
+                // last workerCycle gets executed, state := mWorkerState := PAUSED by us
+                //   (or the workers enters the 'error' state if workerCycle fails)
+                // client gets notified about state change in any case
+                // thread is doing a busy wait while 'state == PAUSED'
+                // client sets mWorkerState := RESUME_REQUESTED
+                // state := mWorkerState (RESUME_REQUESTED)
+                // mWorkerState := RUNNING, but we don't notify the client yet
+                // first workerCycle gets executed, the code below triggers a client notification
+                //   (or if workerCycle fails, worker enters 'error' state and also notifies)
+                // state := mWorkerState (RUNNING)
+                if (state == WorkerState::RESUME_REQUESTED) {
+                    needToNotify = true;
+                }
+                std::lock_guard<std::mutex> lock(mWorkerLock);
+                state = mWorkerState;
+                if (mWorkerState == WorkerState::PAUSE_REQUESTED) {
+                    state = mWorkerState = WorkerState::PAUSED;
+                    needToNotify = true;
+                } else if (mWorkerState == WorkerState::RESUME_REQUESTED) {
+                    mWorkerState = WorkerState::RUNNING;
+                }
+            } else {
+                std::lock_guard<std::mutex> lock(mWorkerLock);
+                if (state == WorkerState::RESUME_REQUESTED ||
+                    mWorkerState == WorkerState::PAUSE_REQUESTED) {
+                    needToNotify = true;
+                }
+                mWorkerState = WorkerState::ERROR;
+                state = WorkerState::STOPPED;
+            }
+            if (needToNotify) {
+                mWorkerCv.notify_one();
+            }
+        }
+    }
+
+    std::thread mWorker;
+    std::mutex mWorkerLock;
+    std::condition_variable mWorkerCv;
+    WorkerState mWorkerState = WorkerState::STOPPED;  // GUARDED_BY(mWorkerLock);
+};
diff --git a/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml
new file mode 100644
index 0000000..6635f31
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/VtsHalAudioV7_0TargetTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalAudioV7_0TargetTest.">
+    <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 class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="setprop vts.native_server.on 1"/>
+        <option name="teardown-command" value="setprop vts.native_server.on 0"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalAudioV7_0TargetTest->/data/local/tmp/VtsHalAudioV7_0TargetTest" />
+        <option name="push" value="audio_policy_configuration_V7_0.xsd->/data/local/tmp/audio_policy_configuration_V7_0.xsd" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalAudioV7_0TargetTest" />
+    </test>
+</configuration>
diff --git a/audio/core/all-versions/vts/functional/tests/HalAudioV6_0GeneratorTest.xml b/audio/core/all-versions/vts/functional/tests/HalAudioV6_0GeneratorTest.xml
new file mode 100644
index 0000000..0c85a05
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/HalAudioV6_0GeneratorTest.xml
@@ -0,0 +1,34 @@
+<?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 HalAudioV6_0GeneratorTest.">
+    <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.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="apm_config_no_vx.xml->/data/local/tmp/apm_config_no_vx.xml" />
+        <option name="push" value="apm_config_with_vx.xml->/data/local/tmp/apm_config_with_vx.xml" />
+        <option name="push" value="HalAudioV6_0GeneratorTest->/data/local/tmp/HalAudioV6_0GeneratorTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="HalAudioV6_0GeneratorTest" />
+    </test>
+</configuration>
diff --git a/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml b/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml
new file mode 100644
index 0000000..2e79455
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/HalAudioV7_0GeneratorTest.xml
@@ -0,0 +1,34 @@
+<?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 HalAudioV7_0GeneratorTest.">
+    <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.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="apm_config_no_vx_7_0.xml->/data/local/tmp/apm_config_no_vx.xml" />
+        <option name="push" value="apm_config_with_vx_7_0.xml->/data/local/tmp/apm_config_with_vx.xml" />
+        <option name="push" value="HalAudioV7_0GeneratorTest->/data/local/tmp/HalAudioV7_0GeneratorTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="HalAudioV7_0GeneratorTest" />
+    </test>
+</configuration>
diff --git a/audio/core/all-versions/vts/functional/tests/apm_config_no_vx.xml b/audio/core/all-versions/vts/functional/tests/apm_config_no_vx.xml
new file mode 100644
index 0000000..61972b2
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/apm_config_no_vx.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <modules>
+        <module name="primary" halVersion="3.0">
+            <attachedDevices>
+                <item>Speaker</item>
+                <item>Built-In Mic</item>
+            </attachedDevices>
+            <defaultOutputDevice>Speaker</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-8400"
+                              maxValueMB="4000"
+                              defaultValueMB="0"
+                              stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Speaker" sources="primary output"/>
+                <route type="mix" sink="primary input" sources="Built-In Mic"/>
+            </routes>
+        </module>
+    </modules>
+    <volumes/>
+    <surroundSound>
+      <formats>
+        <format name="AUDIO_FORMAT_AC3" />
+        <format name="AUDIO_FORMAT_AAC_LC" subformats="AUDIO_FORMAT_AAC_HE_V1 AUDIO_FORMAT_AAC_HE_V2 AUDIO_FORMAT_AAC_ELD AUDIO_FORMAT_AAC_XHE" />
+      </formats>
+    </surroundSound>
+</audioPolicyConfiguration>
diff --git a/audio/core/all-versions/vts/functional/tests/apm_config_no_vx_7_0.xml b/audio/core/all-versions/vts/functional/tests/apm_config_no_vx_7_0.xml
new file mode 100644
index 0000000..abcdb12
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/apm_config_no_vx_7_0.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="7.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <modules>
+        <module name="primary" halVersion="3.0">
+            <attachedDevices>
+                <item>Speaker</item>
+                <item>Built-In Mic</item>
+            </attachedDevices>
+            <defaultOutputDevice>Speaker</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-8400"
+                              maxValueMB="4000"
+                              defaultValueMB="0"
+                              stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Speaker" sources="primary output"/>
+                <route type="mix" sink="primary input" sources="Built-In Mic"/>
+            </routes>
+        </module>
+    </modules>
+    <volumes/>
+    <surroundSound>
+      <formats>
+        <format name="AUDIO_FORMAT_AC3" />
+        <format name="AUDIO_FORMAT_AAC_LC" subformats="AUDIO_FORMAT_AAC_HE_V1 AUDIO_FORMAT_AAC_HE_V2 AUDIO_FORMAT_AAC_ELD AUDIO_FORMAT_AAC_XHE" />
+      </formats>
+    </surroundSound>
+</audioPolicyConfiguration>
diff --git a/audio/core/all-versions/vts/functional/tests/apm_config_with_vx.xml b/audio/core/all-versions/vts/functional/tests/apm_config_with_vx.xml
new file mode 100644
index 0000000..aabb52e
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/apm_config_with_vx.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <modules>
+        <module name="primary" halVersion="3.0">
+            <attachedDevices>
+                <item>Speaker</item>
+                <item>Built-In Mic</item>
+            </attachedDevices>
+            <defaultOutputDevice>Speaker</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                    <profile name="" format="VX_GOOGLE_B_FORMAT"
+                             samplingRates="192000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-8400"
+                              maxValueMB="4000"
+                              defaultValueMB="0"
+                              stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                    <profile name="" format="VX_GOOGLE_B_FORMAT"
+                             samplingRates="192000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+                <devicePort tagName="Ambient Mic" type="VX_GOOGLE_AUDIO_DEVICE_AMBIENT_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000,11025,12000,16000,22050,24000,32000,44100,48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO,AUDIO_CHANNEL_IN_STEREO,AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Speaker" sources="primary output"/>
+                <route type="mix" sink="primary input" sources="Built-In Mic,Ambient Mic"/>
+            </routes>
+        </module>
+    </modules>
+    <volumes/>
+    <surroundSound>
+      <formats>
+        <format name="AUDIO_FORMAT_AC3" />
+        <format name="AUDIO_FORMAT_AAC_LC" subformats="AUDIO_FORMAT_AAC_HE_V1 AUDIO_FORMAT_AAC_HE_V2 AUDIO_FORMAT_AAC_ELD AUDIO_FORMAT_AAC_XHE" />
+        <format name="VX_GOOGLE_B_FORMAT" />
+        <format name="AUDIO_FORMAT_AC4" subformats="VX_GOOGLE_B_FORMAT" />
+      </formats>
+    </surroundSound>
+</audioPolicyConfiguration>
diff --git a/audio/core/all-versions/vts/functional/tests/apm_config_with_vx_7_0.xml b/audio/core/all-versions/vts/functional/tests/apm_config_with_vx_7_0.xml
new file mode 100644
index 0000000..8dd5f45
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/apm_config_with_vx_7_0.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<audioPolicyConfiguration version="7.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <globalConfiguration speaker_drc_enabled="true"/>
+    <modules>
+        <module name="primary" halVersion="3.0">
+            <attachedDevices>
+                <item>Speaker</item>
+                <item>Built-In Mic</item>
+            </attachedDevices>
+            <defaultOutputDevice>Speaker</defaultOutputDevice>
+            <mixPorts>
+                <mixPort name="primary output" role="source" flags="AUDIO_OUTPUT_FLAG_PRIMARY">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                </mixPort>
+                <mixPort name="primary input" role="sink">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                    <profile name="" format="VX_GOOGLE_B_FORMAT"
+                             samplingRates="192000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </mixPort>
+            </mixPorts>
+            <devicePorts>
+                <devicePort tagName="Speaker" role="sink" type="AUDIO_DEVICE_OUT_SPEAKER" address="">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="48000" channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
+                    <gains>
+                        <gain name="gain_1" mode="AUDIO_GAIN_MODE_JOINT"
+                              minValueMB="-8400"
+                              maxValueMB="4000"
+                              defaultValueMB="0"
+                              stepValueMB="100"/>
+                    </gains>
+                </devicePort>
+                <devicePort tagName="Built-In Mic" type="AUDIO_DEVICE_IN_BUILTIN_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                    <profile name="" format="VX_GOOGLE_B_FORMAT"
+                             samplingRates="192000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+                <devicePort tagName="Ambient Mic" type="VX_GOOGLE_AUDIO_DEVICE_AMBIENT_MIC" role="source">
+                    <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
+                             samplingRates="8000 11025 12000 16000 22050 24000 32000 44100 48000"
+                             channelMasks="AUDIO_CHANNEL_IN_MONO AUDIO_CHANNEL_IN_STEREO AUDIO_CHANNEL_IN_FRONT_BACK"/>
+                </devicePort>
+            </devicePorts>
+            <routes>
+                <route type="mix" sink="Speaker" sources="primary output"/>
+                <route type="mix" sink="primary input" sources="Built-In Mic,Ambient Mic"/>
+            </routes>
+        </module>
+    </modules>
+    <volumes/>
+    <surroundSound>
+      <formats>
+        <format name="AUDIO_FORMAT_AC3" />
+        <format name="AUDIO_FORMAT_AAC_LC" subformats="AUDIO_FORMAT_AAC_HE_V1 AUDIO_FORMAT_AAC_HE_V2 AUDIO_FORMAT_AAC_ELD AUDIO_FORMAT_AAC_XHE" />
+        <format name="VX_GOOGLE_B_FORMAT" />
+        <format name="AUDIO_FORMAT_AC4" subformats="VX_GOOGLE_B_FORMAT" />
+      </formats>
+    </surroundSound>
+</audioPolicyConfiguration>
diff --git a/audio/core/all-versions/vts/functional/tests/generators_tests.cpp b/audio/core/all-versions/vts/functional/tests/generators_tests.cpp
new file mode 100644
index 0000000..583ff01
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/generators_tests.cpp
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <android-base/macros.h>
+#include <gtest/gtest.h>
+#define LOG_TAG "Generators_Test"
+#include <log/log.h>
+
+#if MAJOR_VERSION == 6
+#include <system/audio.h>
+#include "6.0/Generators.h"
+#include "PolicyConfig.h"
+#elif MAJOR_VERSION == 7
+#include "7.0/Generators.h"
+#include "7.0/PolicyConfig.h"
+#endif
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+#if MAJOR_VERSION == 7
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+}
+#endif
+
+// Stringify the argument.
+#define QUOTE(x) #x
+#define STRINGIFY(x) QUOTE(x)
+
+struct PolicyConfigManager {
+    static PolicyConfigManager& getInstance() {
+        static PolicyConfigManager instance;
+        return instance;
+    }
+    bool init(const std::string& filePath, const std::string& fileName) {
+        mConfig = std::make_unique<PolicyConfig>(filePath, fileName);
+        mDeviceParameters.clear();
+        if (mConfig->getStatus() == OK) {
+            const auto devices = mConfig->getModulesWithDevicesNames();
+            mDeviceParameters.reserve(devices.size());
+            for (const auto& deviceName : devices) {
+                mDeviceParameters.emplace_back(
+                        "android.hardware.audio.IDevicesFactory@" STRINGIFY(FILE_VERSION),
+                        deviceName);
+            }
+            return true;
+        } else {
+            ALOGE("%s", mConfig->getError().c_str());
+            return false;
+        }
+    }
+    const PolicyConfig& getConfig() { return *mConfig; }
+    const std::vector<DeviceParameter>& getDeviceParameters() { return mDeviceParameters; }
+
+  private:
+    std::unique_ptr<PolicyConfig> mConfig;
+    std::vector<DeviceParameter> mDeviceParameters;
+};
+
+// Test implementations
+const PolicyConfig& getCachedPolicyConfig() {
+    return PolicyConfigManager::getInstance().getConfig();
+}
+
+const std::vector<DeviceParameter>& getDeviceParameters() {
+    return PolicyConfigManager::getInstance().getDeviceParameters();
+}
+
+static const std::string kDataDir = "/data/local/tmp";
+
+class GeneratorsTest : public ::testing::TestWithParam<std::string> {
+  public:
+    static void validateConfig(const AudioConfig& config) {
+#if MAJOR_VERSION == 6
+        ASSERT_TRUE(audio_is_valid_format(static_cast<audio_format_t>(config.format)))
+                << "Audio format is invalid " << ::testing::PrintToString(config.format);
+        ASSERT_TRUE(
+                audio_channel_mask_is_valid(static_cast<audio_channel_mask_t>(config.channelMask)))
+                << "Audio channel mask is invalid " << ::testing::PrintToString(config.channelMask);
+#elif MAJOR_VERSION == 7
+        ASSERT_FALSE(xsd::isUnknownAudioFormat(config.base.format))
+                << "Audio format is invalid " << ::testing::PrintToString(config.base.format);
+        ASSERT_FALSE(xsd::isUnknownAudioChannelMask(config.base.channelMask))
+                << "Audio channel mask is invalid "
+                << ::testing::PrintToString(config.base.channelMask);
+#endif
+    }
+    static void validateDeviceConfigs(const std::vector<DeviceConfigParameter>& params) {
+        for (const auto& param : params) {
+            ASSERT_NO_FATAL_FAILURE(validateConfig(std::get<PARAM_CONFIG>(param)));
+        }
+    }
+};
+
+TEST_P(GeneratorsTest, ValidateConfigs) {
+    ASSERT_TRUE(PolicyConfigManager::getInstance().init(kDataDir, GetParam()));
+    EXPECT_NE(nullptr, getCachedPolicyConfig().getPrimaryModule());
+    EXPECT_FALSE(getCachedPolicyConfig().getModulesWithDevicesNames().empty());
+    const auto allOutConfigs = generateOutputDeviceConfigParameters(false /*oneProfilePerDevice*/);
+    EXPECT_FALSE(allOutConfigs.empty());
+    EXPECT_NO_FATAL_FAILURE(validateDeviceConfigs(allOutConfigs));
+    const auto singleOutConfig = generateOutputDeviceConfigParameters(true /*oneProfilePerDevice*/);
+    EXPECT_FALSE(singleOutConfig.empty());
+    EXPECT_NO_FATAL_FAILURE(validateDeviceConfigs(singleOutConfig));
+    const auto allInConfigs = generateInputDeviceConfigParameters(false /*oneProfilePerDevice*/);
+    EXPECT_FALSE(allInConfigs.empty());
+    EXPECT_NO_FATAL_FAILURE(validateDeviceConfigs(allInConfigs));
+    const auto singleInConfig = generateInputDeviceConfigParameters(true /*oneProfilePerDevice*/);
+    EXPECT_FALSE(singleInConfig.empty());
+    EXPECT_NO_FATAL_FAILURE(validateDeviceConfigs(singleInConfig));
+}
+
+// Target file names are the same for all versions, see 'HalAudioVx_0GeneratorTest.xml' test configs
+INSTANTIATE_TEST_SUITE_P(Generators, GeneratorsTest,
+                         ::testing::Values("apm_config_no_vx.xml", "apm_config_with_vx.xml"));
diff --git a/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
new file mode 100644
index 0000000..925fd33
--- /dev/null
+++ b/audio/core/all-versions/vts/functional/tests/streamworker_tests.cpp
@@ -0,0 +1,210 @@
+/*
+ * 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.
+ */
+
+#include "StreamWorker.h"
+
+#include <sched.h>
+#include <unistd.h>
+#include <atomic>
+
+#include <gtest/gtest.h>
+#define LOG_TAG "StreamWorker_Test"
+#include <log/log.h>
+
+struct TestStream {
+    std::atomic<bool> error = false;
+};
+
+class TestWorker : public StreamWorker<TestWorker> {
+  public:
+    // Use nullptr to test error reporting from the worker thread.
+    explicit TestWorker(TestStream* stream) : mStream(stream) {}
+
+    size_t getWorkerCycles() const { return mWorkerCycles; }
+    bool hasWorkerCycleCalled() const { return mWorkerCycles != 0; }
+    bool hasNoWorkerCycleCalled(useconds_t usec) {
+        const size_t cyclesBefore = mWorkerCycles;
+        usleep(usec);
+        return mWorkerCycles == cyclesBefore;
+    }
+
+    bool workerInit() { return mStream; }
+    bool workerCycle() {
+        do {
+            mWorkerCycles++;
+        } while (mWorkerCycles == 0);
+        return !mStream->error;
+    }
+
+  private:
+    TestStream* const mStream;
+    std::atomic<size_t> mWorkerCycles = 0;
+};
+
+// The parameter specifies whether an extra call to 'stop' is made at the end.
+class StreamWorkerInvalidTest : public testing::TestWithParam<bool> {
+  public:
+    StreamWorkerInvalidTest() : StreamWorkerInvalidTest(nullptr) {}
+    void TearDown() override {
+        if (GetParam()) {
+            worker.stop();
+        }
+    }
+
+  protected:
+    StreamWorkerInvalidTest(TestStream* stream) : testing::TestWithParam<bool>(), worker(stream) {}
+    TestWorker worker;
+};
+
+TEST_P(StreamWorkerInvalidTest, Uninitialized) {
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedPauseIgnored) {
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, UninitializedResumeIgnored) {
+    EXPECT_FALSE(worker.hasError());
+    worker.resume();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, Start) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, PauseIgnored) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_TRUE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerInvalidTest, ResumeIgnored) {
+    EXPECT_FALSE(worker.start());
+    EXPECT_TRUE(worker.hasError());
+    worker.resume();
+    EXPECT_TRUE(worker.hasError());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorkerInvalid, StreamWorkerInvalidTest, testing::Bool());
+
+class StreamWorkerTest : public StreamWorkerInvalidTest {
+  public:
+    StreamWorkerTest() : StreamWorkerInvalidTest(&stream) {}
+
+  protected:
+    TestStream stream;
+};
+
+static constexpr unsigned kWorkerIdleCheckTime = 50 * 1000;
+
+TEST_P(StreamWorkerTest, Uninitialized) {
+    EXPECT_FALSE(worker.hasWorkerCycleCalled());
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, Start) {
+    ASSERT_TRUE(worker.start());
+    worker.waitForAtLeastOneCycle();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerError) {
+    ASSERT_TRUE(worker.start());
+    stream.error = true;
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, PauseResume) {
+    ASSERT_TRUE(worker.start());
+    worker.waitForAtLeastOneCycle();
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_FALSE(worker.hasError());
+    const size_t workerCyclesBefore = worker.getWorkerCycles();
+    worker.resume();
+    // 'resume' is synchronous and returns after the worker has looped at least once.
+    EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, StopPaused) {
+    ASSERT_TRUE(worker.start());
+    worker.waitForAtLeastOneCycle();
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    worker.stop();
+    EXPECT_FALSE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, PauseAfterErrorIgnored) {
+    ASSERT_TRUE(worker.start());
+    stream.error = true;
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    worker.pause();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, ResumeAfterErrorIgnored) {
+    ASSERT_TRUE(worker.start());
+    stream.error = true;
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    worker.resume();
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+    EXPECT_TRUE(worker.hasError());
+}
+
+TEST_P(StreamWorkerTest, WorkerErrorOnResume) {
+    ASSERT_TRUE(worker.start());
+    worker.waitForAtLeastOneCycle();
+    EXPECT_FALSE(worker.hasError());
+    worker.pause();
+    EXPECT_FALSE(worker.hasError());
+    stream.error = true;
+    EXPECT_FALSE(worker.hasError());
+    worker.resume();
+    worker.waitForAtLeastOneCycle();
+    EXPECT_TRUE(worker.hasError());
+    EXPECT_TRUE(worker.hasNoWorkerCycleCalled(kWorkerIdleCheckTime));
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycle) {
+    ASSERT_TRUE(worker.start());
+    const size_t workerCyclesBefore = worker.getWorkerCycles();
+    EXPECT_TRUE(worker.waitForAtLeastOneCycle());
+    EXPECT_GT(worker.getWorkerCycles(), workerCyclesBefore);
+}
+
+TEST_P(StreamWorkerTest, WaitForAtLeastOneCycleError) {
+    ASSERT_TRUE(worker.start());
+    stream.error = true;
+    EXPECT_FALSE(worker.waitForAtLeastOneCycle());
+}
+
+INSTANTIATE_TEST_SUITE_P(StreamWorker, StreamWorkerTest, testing::Bool());
diff --git a/audio/effect/2.0/Android.bp b/audio/effect/2.0/Android.bp
index 7b37260..f2f5124 100644
--- a/audio/effect/2.0/Android.bp
+++ b/audio/effect/2.0/Android.bp
@@ -1,13 +1,19 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.effect@2.0",
     root: "android.hardware",
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAcousticEchoCancelerEffect.hal",
diff --git a/audio/effect/2.0/xml/Android.bp b/audio/effect/2.0/xml/Android.bp
index 050425a..d015639 100644
--- a/audio/effect/2.0/xml/Android.bp
+++ b/audio/effect/2.0/xml/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 genrule {
     name: "audio_effects_conf_V2_0",
     srcs: ["audio_effects_conf.xsd"],
diff --git a/audio/effect/4.0/Android.bp b/audio/effect/4.0/Android.bp
index 2242d6d..1eb754a 100644
--- a/audio/effect/4.0/Android.bp
+++ b/audio/effect/4.0/Android.bp
@@ -1,13 +1,19 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.effect@4.0",
     root: "android.hardware",
     // TODO(b/153609531): remove when no longer needed.
     native_bridge_supported: true,
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAcousticEchoCancelerEffect.hal",
diff --git a/audio/effect/4.0/xml/Android.bp b/audio/effect/4.0/xml/Android.bp
index 27ffd02..8c03a35 100644
--- a/audio/effect/4.0/xml/Android.bp
+++ b/audio/effect/4.0/xml/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 genrule {
     name: "audio_effects_conf_V4_0",
     srcs: ["audio_effects_conf.xsd"],
diff --git a/audio/effect/5.0/Android.bp b/audio/effect/5.0/Android.bp
index b7dad8d..126964c 100644
--- a/audio/effect/5.0/Android.bp
+++ b/audio/effect/5.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.effect@5.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAcousticEchoCancelerEffect.hal",
diff --git a/audio/effect/5.0/xml/Android.bp b/audio/effect/5.0/xml/Android.bp
index 67b2f97..7982e2a 100644
--- a/audio/effect/5.0/xml/Android.bp
+++ b/audio/effect/5.0/xml/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_effects_conf_V5_0",
     srcs: ["audio_effects_conf.xsd"],
diff --git a/audio/effect/6.0/Android.bp b/audio/effect/6.0/Android.bp
index b6184f3..8d15d09 100644
--- a/audio/effect/6.0/Android.bp
+++ b/audio/effect/6.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.audio.effect@6.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAcousticEchoCancelerEffect.hal",
diff --git a/audio/effect/6.0/xml/Android.bp b/audio/effect/6.0/xml/Android.bp
index 8d68672..f139341 100644
--- a/audio/effect/6.0/xml/Android.bp
+++ b/audio/effect/6.0/xml/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_effects_conf_V6_0",
     srcs: ["audio_effects_conf.xsd"],
diff --git a/audio/effect/7.0/Android.bp b/audio/effect/7.0/Android.bp
new file mode 100644
index 0000000..7399cdb
--- /dev/null
+++ b/audio/effect/7.0/Android.bp
@@ -0,0 +1,39 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.audio.effect@7.0",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IAcousticEchoCancelerEffect.hal",
+        "IAutomaticGainControlEffect.hal",
+        "IBassBoostEffect.hal",
+        "IDownmixEffect.hal",
+        "IEffect.hal",
+        "IEffectBufferProviderCallback.hal",
+        "IEffectsFactory.hal",
+        "IEnvironmentalReverbEffect.hal",
+        "IEqualizerEffect.hal",
+        "ILoudnessEnhancerEffect.hal",
+        "INoiseSuppressionEffect.hal",
+        "IPresetReverbEffect.hal",
+        "IVirtualizerEffect.hal",
+        "IVisualizerEffect.hal",
+    ],
+    interfaces: [
+        "android.hardware.audio.common@7.0",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
+    ],
+    gen_java: false,
+    gen_java_constants: true,
+}
diff --git a/audio/effect/7.0/IAcousticEchoCancelerEffect.hal b/audio/effect/7.0/IAcousticEchoCancelerEffect.hal
new file mode 100644
index 0000000..2bc2a7f
--- /dev/null
+++ b/audio/effect/7.0/IAcousticEchoCancelerEffect.hal
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IAcousticEchoCancelerEffect extends IEffect {
+    /**
+     * Sets echo delay value in milliseconds.
+     */
+    setEchoDelay(uint32_t echoDelayMs) generates (Result retval);
+
+    /**
+     * Gets echo delay value in milliseconds.
+     */
+    getEchoDelay() generates (Result retval, uint32_t echoDelayMs);
+};
diff --git a/audio/effect/7.0/IAutomaticGainControlEffect.hal b/audio/effect/7.0/IAutomaticGainControlEffect.hal
new file mode 100644
index 0000000..8ffa659
--- /dev/null
+++ b/audio/effect/7.0/IAutomaticGainControlEffect.hal
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IAutomaticGainControlEffect extends IEffect {
+    /**
+     * Sets target level in millibels.
+     */
+    setTargetLevel(int16_t targetLevelMb) generates (Result retval);
+
+    /**
+     * Gets target level.
+     */
+    getTargetLevel() generates (Result retval, int16_t targetLevelMb);
+
+    /**
+     * Sets gain in the compression range in millibels.
+     */
+    setCompGain(int16_t compGainMb) generates (Result retval);
+
+    /**
+     * Gets gain in the compression range.
+     */
+    getCompGain() generates (Result retval, int16_t compGainMb);
+
+    /**
+     * Enables or disables limiter.
+     */
+    setLimiterEnabled(bool enabled) generates (Result retval);
+
+    /**
+     * Returns whether limiter is enabled.
+     */
+    isLimiterEnabled() generates (Result retval, bool enabled);
+
+    struct AllProperties {
+        int16_t targetLevelMb;
+        int16_t compGainMb;
+        bool limiterEnabled;
+    };
+
+    /**
+     * Sets all properties at once.
+     */
+    setAllProperties(AllProperties properties) generates (Result retval);
+
+    /**
+     * Gets all properties at once.
+     */
+    getAllProperties() generates (Result retval, AllProperties properties);
+};
diff --git a/audio/effect/7.0/IBassBoostEffect.hal b/audio/effect/7.0/IBassBoostEffect.hal
new file mode 100644
index 0000000..d8d049e
--- /dev/null
+++ b/audio/effect/7.0/IBassBoostEffect.hal
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IBassBoostEffect extends IEffect {
+    /**
+     * Returns whether setting bass boost strength is supported.
+     */
+    isStrengthSupported() generates (Result retval, bool strengthSupported);
+
+    enum StrengthRange : uint16_t {
+        MIN = 0,
+        MAX = 1000
+    };
+
+    /**
+     * Sets bass boost strength.
+     *
+     * @param strength strength of the effect. The valid range for strength
+     *                 strength is [0, 1000], where 0 per mille designates the
+     *                 mildest effect and 1000 per mille designates the
+     *                 strongest.
+     * @return retval operation completion status.
+     */
+    setStrength(uint16_t strength) generates (Result retval);
+
+    /**
+     * Gets virtualization strength.
+     */
+    getStrength() generates (Result retval, uint16_t strength);
+};
diff --git a/audio/effect/7.0/IDownmixEffect.hal b/audio/effect/7.0/IDownmixEffect.hal
new file mode 100644
index 0000000..2035430
--- /dev/null
+++ b/audio/effect/7.0/IDownmixEffect.hal
@@ -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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IDownmixEffect extends IEffect {
+    enum Type : int32_t {
+        STRIP, // throw away the extra channels
+        FOLD   // mix the extra channels with FL/FR
+    };
+
+    /**
+     * Sets the current downmix preset.
+     */
+    setType(Type preset) generates (Result retval);
+
+    /**
+     * Gets the current downmix preset.
+     */
+    getType() generates (Result retval, Type preset);
+};
diff --git a/audio/effect/7.0/IEffect.hal b/audio/effect/7.0/IEffect.hal
new file mode 100644
index 0000000..aa94f6d
--- /dev/null
+++ b/audio/effect/7.0/IEffect.hal
@@ -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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffectBufferProviderCallback;
+
+interface IEffect {
+    /**
+     * Initialize effect engine--all configurations return to default.
+     *
+     * @return retval operation completion status.
+     */
+    @entry
+    init() generates (Result retval);
+
+    /**
+     * Apply new audio parameters configurations for input and output buffers.
+     * The provider callbacks may be empty, but in this case the buffer
+     * must be provided in the EffectConfig structure.
+     *
+     * @param config configuration descriptor.
+     * @param inputBufferProvider optional buffer provider reference.
+     * @param outputBufferProvider optional buffer provider reference.
+     * @return retval operation completion status.
+     */
+    setConfig(EffectConfig config,
+            IEffectBufferProviderCallback inputBufferProvider,
+            IEffectBufferProviderCallback outputBufferProvider)
+            generates (Result retval);
+
+    /**
+     * Reset the effect engine. Keep configuration but resets state and buffer
+     * content.
+     *
+     * @return retval operation completion status.
+     */
+    reset() generates (Result retval);
+
+    /**
+     * Enable processing.
+     *
+     * @return retval operation completion status.
+     */
+    enable() generates (Result retval);
+
+    /**
+     * Disable processing.
+     *
+     * @return retval operation completion status.
+     */
+    disable() generates (Result retval);
+
+    /**
+     * Set the rendering device the audio output path is connected to.  The
+     * effect implementation must set EFFECT_FLAG_DEVICE_IND flag in its
+     * descriptor to receive this command when the device changes.
+     *
+     * Note: this method is only supported for effects inserted into
+     *       the output chain.
+     *
+     * @param device output device specification.
+     * @return retval operation completion status.
+     */
+    setDevice(DeviceAddress device) generates (Result retval);
+
+    /**
+     * Set and get volume. Used by audio framework to delegate volume control to
+     * effect engine. The effect implementation must set EFFECT_FLAG_VOLUME_CTRL
+     * flag in its descriptor to receive this command. The effect engine must
+     * return the volume that should be applied before the effect is
+     * processed. The overall volume (the volume actually applied by the effect
+     * engine multiplied by the returned value) should match the value indicated
+     * in the command.
+     *
+     * @param volumes vector containing volume for each channel defined in
+     *                EffectConfig for output buffer expressed in 8.24 fixed
+     *                point format.
+     * @return result updated volume values.
+     * @return retval operation completion status.
+     */
+    setAndGetVolume(vec<uint32_t> volumes)
+            generates (Result retval, vec<uint32_t> result);
+
+    /**
+     * Notify the effect of the volume change. The effect implementation must
+     * set EFFECT_FLAG_VOLUME_IND flag in its descriptor to receive this
+     * command.
+     *
+     * @param volumes vector containing volume for each channel defined in
+     *                EffectConfig for output buffer expressed in 8.24 fixed
+     *                point format.
+     * @return retval operation completion status.
+     */
+    volumeChangeNotification(vec<uint32_t> volumes)
+            generates (Result retval);
+
+    /**
+     * Set the audio mode. The effect implementation must set
+     * EFFECT_FLAG_AUDIO_MODE_IND flag in its descriptor to receive this command
+     * when the audio mode changes.
+     *
+     * @param mode desired audio mode.
+     * @return retval operation completion status.
+     */
+    setAudioMode(AudioMode mode) generates (Result retval);
+
+    /**
+     * Apply new audio parameters configurations for input and output buffers of
+     * reverse stream.  An example of reverse stream is the echo reference
+     * supplied to an Acoustic Echo Canceler.
+     *
+     * @param config configuration descriptor.
+     * @param inputBufferProvider optional buffer provider reference.
+     * @param outputBufferProvider optional buffer provider reference.
+     * @return retval operation completion status.
+     */
+    setConfigReverse(EffectConfig config,
+            IEffectBufferProviderCallback inputBufferProvider,
+            IEffectBufferProviderCallback outputBufferProvider)
+            generates (Result retval);
+
+    /**
+     * Set the capture device the audio input path is connected to. The effect
+     * implementation must set EFFECT_FLAG_DEVICE_IND flag in its descriptor to
+     * receive this command when the device changes.
+     *
+     * Note: this method is only supported for effects inserted into
+     *       the input chain.
+     *
+     * @param device input device specification.
+     * @return retval operation completion status.
+     */
+    setInputDevice(DeviceAddress device) generates (Result retval);
+
+    /**
+     * Read audio parameters configurations for input and output buffers.
+     *
+     * @return retval operation completion status.
+     * @return config configuration descriptor.
+     */
+    getConfig() generates (Result retval, EffectConfig config);
+
+    /**
+     * Read audio parameters configurations for input and output buffers of
+     * reverse stream.
+     *
+     * @return retval operation completion status.
+     * @return config configuration descriptor.
+     */
+    getConfigReverse() generates (Result retval, EffectConfig config);
+
+    /**
+     * Queries for supported combinations of main and auxiliary channels
+     * (e.g. for a multi-microphone noise suppressor).
+     *
+     * @param maxConfigs maximum number of the combinations to return.
+     * @return retval absence of the feature support is indicated using
+     *                NOT_SUPPORTED code. RESULT_TOO_BIG is returned if
+     *                the number of supported combinations exceeds 'maxConfigs'.
+     * @return result list of configuration descriptors.
+     */
+    getSupportedAuxChannelsConfigs(uint32_t maxConfigs)
+            generates (Result retval, vec<EffectAuxChannelsConfig> result);
+
+    /**
+     * Retrieves the current configuration of main and auxiliary channels.
+     *
+     * @return retval absence of the feature support is indicated using
+     *                NOT_SUPPORTED code.
+     * @return result configuration descriptor.
+     */
+    getAuxChannelsConfig()
+            generates (Result retval, EffectAuxChannelsConfig result);
+
+    /**
+     * Sets the current configuration of main and auxiliary channels.
+     *
+     * @return retval operation completion status; absence of the feature
+     *                support is indicated using NOT_SUPPORTED code.
+     */
+    setAuxChannelsConfig(EffectAuxChannelsConfig config)
+            generates (Result retval);
+
+    /**
+     * Set the audio source the capture path is configured for (Camcorder, voice
+     * recognition...).
+     *
+     * Note: this method is only supported for effects inserted into
+     *       the input chain.
+     *
+     * @param source source descriptor.
+     * @return retval operation completion status.
+     */
+    setAudioSource(AudioSource source) generates (Result retval);
+
+    /**
+     * This command indicates if the playback thread the effect is attached to
+     * is offloaded or not, and updates the I/O handle of the playback thread
+     * the effect is attached to.
+     *
+     * @param param effect offload descriptor.
+     * @return retval operation completion status.
+     */
+    offload(EffectOffloadParameter param) generates (Result retval);
+
+    /**
+     * Returns the effect descriptor.
+     *
+     * @return retval operation completion status.
+     * @return descriptor effect descriptor.
+     */
+    getDescriptor() generates (Result retval, EffectDescriptor descriptor);
+
+    /**
+     * Set up required transports for passing audio buffers to the effect.
+     *
+     * The transport consists of shared memory and a message queue for reporting
+     * effect processing operation status. The shared memory is set up
+     * separately using 'setProcessBuffers' method.
+     *
+     * Processing is requested by setting 'REQUEST_PROCESS' or
+     * 'REQUEST_PROCESS_REVERSE' EventFlags associated with the status message
+     * queue. The result of processing may be one of the following:
+     *   OK if there were no errors during processing;
+     *   INVALID_ARGUMENTS if audio buffers are invalid;
+     *   INVALID_STATE if the engine has finished the disable phase;
+     *   NOT_INITIALIZED if the audio buffers were not set;
+     *   NOT_SUPPORTED if the requested processing type is not supported by
+     *                 the effect.
+     *
+     * @return retval OK if both message queues were created successfully.
+     *                INVALID_STATE if the method was already called.
+     *                INVALID_ARGUMENTS if there was a problem setting up
+     *                                  the queue.
+     * @return statusMQ a message queue used for passing status from the effect.
+     */
+    prepareForProcessing() generates (Result retval, fmq_sync<Result> statusMQ);
+
+    /**
+     * Set up input and output buffers for processing audio data. The effect
+     * may modify both the input and the output buffer during the operation.
+     * Buffers may be set multiple times during effect lifetime.
+     *
+     * The input and the output buffer may be reused between different effects,
+     * and the input buffer may be used as an output buffer. Buffers are
+     * distinguished using 'AudioBuffer.id' field.
+     *
+     * @param inBuffer input audio buffer.
+     * @param outBuffer output audio buffer.
+     * @return retval OK if both buffers were mapped successfully.
+     *                INVALID_ARGUMENTS if there was a problem with mapping
+     *                                  any of the buffers.
+     */
+    setProcessBuffers(AudioBuffer inBuffer, AudioBuffer outBuffer)
+            generates (Result retval);
+
+    /**
+     * Execute a vendor specific command on the effect. The command code
+     * and data, as well as result data are not interpreted by Android
+     * Framework and are passed as-is between the application and the effect.
+     *
+     * The effect must use standard POSIX.1-2001 error codes for the operation
+     * completion status.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it. This method only works for effects
+     * implemented in software.
+     *
+     * @param commandId the ID of the command.
+     * @param data command data.
+     * @param resultMaxSize maximum size in bytes of the result; can be 0.
+     * @return status command completion status.
+     * @return result result data.
+     */
+    command(uint32_t commandId, vec<uint8_t> data, uint32_t resultMaxSize)
+            generates (int32_t status, vec<uint8_t> result);
+
+    /**
+     * Set a vendor-specific parameter and apply it immediately. The parameter
+     * code and data are not interpreted by Android Framework and are passed
+     * as-is between the application and the effect.
+     *
+     * The effect must use INVALID_ARGUMENTS return code if the parameter ID is
+     * unknown or if provided parameter data is invalid. If the effect does not
+     * support setting vendor-specific parameters, it must return NOT_SUPPORTED.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it. This method only works for effects
+     * implemented in software.
+     *
+     * @param parameter identifying data of the parameter.
+     * @param value the value of the parameter.
+     * @return retval operation completion status.
+     */
+    setParameter(vec<uint8_t> parameter, vec<uint8_t> value)
+            generates (Result retval);
+
+    /**
+     * Get a vendor-specific parameter value. The parameter code and returned
+     * data are not interpreted by Android Framework and are passed as-is
+     * between the application and the effect.
+     *
+     * The effect must use INVALID_ARGUMENTS return code if the parameter ID is
+     * unknown. If the effect does not support setting vendor-specific
+     * parameters, it must return NOT_SUPPORTED.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it.  This method only works for effects
+     * implemented in software.
+     *
+     * @param parameter identifying data of the parameter.
+     * @param valueMaxSize maximum size in bytes of the value.
+     * @return retval operation completion status.
+     * @return result the value of the parameter.
+     */
+    getParameter(vec<uint8_t> parameter, uint32_t valueMaxSize)
+            generates (Result retval, vec<uint8_t> value);
+
+    /**
+     * Get supported configs for a vendor-specific feature. The configs returned
+     * are not interpreted by Android Framework and are passed as-is between the
+     * application and the effect.
+     *
+     * The effect must use INVALID_ARGUMENTS return code if the feature ID is
+     * unknown. If the effect does not support getting vendor-specific feature
+     * configs, it must return NOT_SUPPORTED. If the feature is supported but
+     * the total number of supported configurations exceeds the maximum number
+     * indicated by the caller, the method must return RESULT_TOO_BIG.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it.  This method only works for effects
+     * implemented in software.
+     *
+     * @param featureId feature identifier.
+     * @param maxConfigs maximum number of configs to return.
+     * @param configSize size of each config in bytes.
+     * @return retval operation completion status.
+     * @return configsCount number of configs returned.
+     * @return configsData data for all the configs returned.
+     */
+    getSupportedConfigsForFeature(
+            uint32_t featureId,
+            uint32_t maxConfigs,
+            uint32_t configSize) generates (
+                    Result retval,
+                    uint32_t configsCount,
+                    vec<uint8_t> configsData);
+
+    /**
+     * Get the current config for a vendor-specific feature. The config returned
+     * is not interpreted by Android Framework and is passed as-is between the
+     * application and the effect.
+     *
+     * The effect must use INVALID_ARGUMENTS return code if the feature ID is
+     * unknown. If the effect does not support getting vendor-specific
+     * feature configs, it must return NOT_SUPPORTED.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it.  This method only works for effects
+     * implemented in software.
+     *
+     * @param featureId feature identifier.
+     * @param configSize size of the config in bytes.
+     * @return retval operation completion status.
+     * @return configData config data.
+     */
+    getCurrentConfigForFeature(uint32_t featureId, uint32_t configSize)
+            generates (Result retval, vec<uint8_t> configData);
+
+    /**
+     * Set the current config for a vendor-specific feature. The config data
+     * is not interpreted by Android Framework and is passed as-is between the
+     * application and the effect.
+     *
+     * The effect must use INVALID_ARGUMENTS return code if the feature ID is
+     * unknown. If the effect does not support getting vendor-specific
+     * feature configs, it must return NOT_SUPPORTED.
+     *
+     * Use this method only if the effect is provided by a third party, and
+     * there is no interface defined for it.  This method only works for effects
+     * implemented in software.
+     *
+     * @param featureId feature identifier.
+     * @param configData config data.
+     * @return retval operation completion status.
+     */
+    setCurrentConfigForFeature(uint32_t featureId, vec<uint8_t> configData)
+            generates (Result retval);
+
+    /**
+     * Called by the framework to deinitialize the effect and free up
+     * all currently allocated resources. It is recommended to close
+     * the effect on the client side as soon as it is becomes unused.
+     *
+     * The client must ensure that this function is not called while
+     * audio data is being transferred through the effect's message queues.
+     *
+     * @return retval OK in case the success.
+     *                INVALID_STATE if the effect was already closed.
+     */
+    close() generates (Result retval);
+};
diff --git a/audio/effect/7.0/IEffectBufferProviderCallback.hal b/audio/effect/7.0/IEffectBufferProviderCallback.hal
new file mode 100644
index 0000000..d18f7df
--- /dev/null
+++ b/audio/effect/7.0/IEffectBufferProviderCallback.hal
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+/**
+ * This callback interface contains functions that can be used by the effect
+ * engine 'process' function to exchange input and output audio buffers.
+ */
+interface IEffectBufferProviderCallback {
+    /**
+     * Called to retrieve a buffer where data should read from by 'process'
+     * function.
+     *
+     * @return buffer audio buffer for processing
+     */
+    getBuffer() generates (AudioBuffer buffer);
+
+    /**
+     * Called to provide a buffer with the data written by 'process' function.
+     *
+     * @param buffer audio buffer for processing
+     */
+    putBuffer(AudioBuffer buffer);
+};
diff --git a/audio/effect/7.0/IEffectsFactory.hal b/audio/effect/7.0/IEffectsFactory.hal
new file mode 100644
index 0000000..337251c
--- /dev/null
+++ b/audio/effect/7.0/IEffectsFactory.hal
@@ -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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IEffectsFactory {
+    /**
+     * Returns descriptors of different effects in all loaded libraries.
+     *
+     * @return retval operation completion status.
+     * @return result list of effect descriptors.
+     */
+    getAllDescriptors() generates(Result retval, vec<EffectDescriptor> result);
+
+    /**
+     * Returns a descriptor of a particular effect.
+     *
+     * @return retval operation completion status.
+     * @return result effect descriptor.
+     */
+    getDescriptor(Uuid uid) generates(Result retval, EffectDescriptor result);
+
+    /**
+     * Creates an effect engine of the specified type.  To release the effect
+     * engine, it is necessary to release references to the returned effect
+     * object.
+     *
+     * @param uid effect uuid.
+     * @param session audio session to which this effect instance will be
+     *                attached.  All effects created with the same session ID
+     *                are connected in series and process the same signal
+     *                stream.
+     * @param ioHandle identifies the output or input stream this effect is
+     *                 directed to in audio HAL.
+     * @param device identifies the sink or source device this effect is directed to in the
+     *               audio HAL. Must be specified if session is AudioSessionConsts.DEVICE.
+     *               "device" is the AudioPortHandle used for the device when the audio
+     *               patch is created at the audio HAL.
+     * @return retval operation completion status.
+     * @return result the interface for the created effect.
+     * @return effectId the unique ID of the effect to be used with
+     *                  IStream::addEffect and IStream::removeEffect methods.
+     */
+    createEffect(Uuid uid, AudioSession session, AudioIoHandle ioHandle, AudioPortHandle device)
+        generates (Result retval, IEffect result, uint64_t effectId);
+};
diff --git a/audio/effect/7.0/IEnvironmentalReverbEffect.hal b/audio/effect/7.0/IEnvironmentalReverbEffect.hal
new file mode 100644
index 0000000..e02cfbc
--- /dev/null
+++ b/audio/effect/7.0/IEnvironmentalReverbEffect.hal
@@ -0,0 +1,178 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IEnvironmentalReverbEffect extends IEffect {
+    /**
+     * Sets whether the effect should be bypassed.
+     */
+    setBypass(bool bypass) generates (Result retval);
+
+    /**
+     * Gets whether the effect should be bypassed.
+     */
+    getBypass() generates (Result retval, bool bypass);
+
+    enum ParamRange : int16_t {
+        ROOM_LEVEL_MIN = -6000,
+        ROOM_LEVEL_MAX = 0,
+        ROOM_HF_LEVEL_MIN = -4000,
+        ROOM_HF_LEVEL_MAX = 0,
+        DECAY_TIME_MIN = 100,
+        DECAY_TIME_MAX = 20000,
+        DECAY_HF_RATIO_MIN = 100,
+        DECAY_HF_RATIO_MAX = 1000,
+        REFLECTIONS_LEVEL_MIN = -6000,
+        REFLECTIONS_LEVEL_MAX = 0,
+        REFLECTIONS_DELAY_MIN = 0,
+        REFLECTIONS_DELAY_MAX = 65,
+        REVERB_LEVEL_MIN = -6000,
+        REVERB_LEVEL_MAX = 0,
+        REVERB_DELAY_MIN = 0,
+        REVERB_DELAY_MAX = 65,
+        DIFFUSION_MIN = 0,
+        DIFFUSION_MAX = 1000,
+        DENSITY_MIN = 0,
+        DENSITY_MAX = 1000
+    };
+
+    /**
+     * Sets the room level.
+     */
+    setRoomLevel(int16_t roomLevel) generates (Result retval);
+
+    /**
+     * Gets the room level.
+     */
+    getRoomLevel() generates (Result retval, int16_t roomLevel);
+
+    /**
+     * Sets the room high frequencies level.
+     */
+    setRoomHfLevel(int16_t roomHfLevel) generates (Result retval);
+
+    /**
+     * Gets the room high frequencies level.
+     */
+    getRoomHfLevel() generates (Result retval, int16_t roomHfLevel);
+
+    /**
+     * Sets the room decay time.
+     */
+    setDecayTime(uint32_t decayTime) generates (Result retval);
+
+    /**
+     * Gets the room decay time.
+     */
+    getDecayTime() generates (Result retval, uint32_t decayTime);
+
+    /**
+     * Sets the ratio of high frequencies decay.
+     */
+    setDecayHfRatio(int16_t decayHfRatio) generates (Result retval);
+
+    /**
+     * Gets the ratio of high frequencies decay.
+     */
+    getDecayHfRatio() generates (Result retval, int16_t decayHfRatio);
+
+    /**
+     * Sets the level of reflections in the room.
+     */
+    setReflectionsLevel(int16_t reflectionsLevel) generates (Result retval);
+
+    /**
+     * Gets the level of reflections in the room.
+     */
+    getReflectionsLevel() generates (Result retval, int16_t reflectionsLevel);
+
+    /**
+     * Sets the reflections delay in the room.
+     */
+    setReflectionsDelay(uint32_t reflectionsDelay) generates (Result retval);
+
+    /**
+     * Gets the reflections delay in the room.
+     */
+    getReflectionsDelay() generates (Result retval, uint32_t reflectionsDelay);
+
+    /**
+     * Sets the reverb level of the room.
+     */
+    setReverbLevel(int16_t reverbLevel) generates (Result retval);
+
+    /**
+     * Gets the reverb level of the room.
+     */
+    getReverbLevel() generates (Result retval, int16_t reverbLevel);
+
+    /**
+     * Sets the reverb delay of the room.
+     */
+    setReverbDelay(uint32_t reverDelay) generates (Result retval);
+
+    /**
+     * Gets the reverb delay of the room.
+     */
+    getReverbDelay() generates (Result retval, uint32_t reverbDelay);
+
+    /**
+     * Sets room diffusion.
+     */
+    setDiffusion(int16_t diffusion) generates (Result retval);
+
+    /**
+     * Gets room diffusion.
+     */
+    getDiffusion() generates (Result retval, int16_t diffusion);
+
+    /**
+     * Sets room wall density.
+     */
+    setDensity(int16_t density) generates (Result retval);
+
+    /**
+     * Gets room wall density.
+     */
+    getDensity() generates (Result retval, int16_t density);
+
+    struct AllProperties {
+        int16_t  roomLevel;         // in millibels,    range -6000 to 0
+        int16_t  roomHfLevel;       // in millibels,    range -4000 to 0
+        uint32_t decayTime;         // in milliseconds, range 100 to 20000
+        int16_t  decayHfRatio;      // in permilles,    range 100 to 1000
+        int16_t  reflectionsLevel;  // in millibels,    range -6000 to 0
+        uint32_t reflectionsDelay;  // in milliseconds, range 0 to 65
+        int16_t  reverbLevel;       // in millibels,    range -6000 to 0
+        uint32_t reverbDelay;       // in milliseconds, range 0 to 65
+        int16_t  diffusion;         // in permilles,    range 0 to 1000
+        int16_t  density;           // in permilles,    range 0 to 1000
+    };
+
+    /**
+     * Sets all properties at once.
+     */
+    setAllProperties(AllProperties properties) generates (Result retval);
+
+    /**
+     * Gets all properties at once.
+     */
+    getAllProperties() generates (Result retval, AllProperties properties);
+};
diff --git a/audio/effect/7.0/IEqualizerEffect.hal b/audio/effect/7.0/IEqualizerEffect.hal
new file mode 100644
index 0000000..e7d7ae1
--- /dev/null
+++ b/audio/effect/7.0/IEqualizerEffect.hal
@@ -0,0 +1,93 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IEqualizerEffect extends IEffect {
+    /**
+     * Gets the number of frequency bands that the equalizer supports.
+     */
+    getNumBands() generates (Result retval, uint16_t numBands);
+
+    /**
+     * Returns the minimum and maximum band levels supported.
+     */
+    getLevelRange()
+            generates (Result retval, int16_t minLevel, int16_t maxLevel);
+
+    /**
+     * Sets the gain for the given equalizer band.
+     */
+    setBandLevel(uint16_t band, int16_t level) generates (Result retval);
+
+    /**
+     * Gets the gain for the given equalizer band.
+     */
+    getBandLevel(uint16_t band) generates (Result retval, int16_t level);
+
+    /**
+     * Gets the center frequency of the given band, in milliHertz.
+     */
+    getBandCenterFrequency(uint16_t band)
+            generates (Result retval, uint32_t centerFreqmHz);
+
+    /**
+     * Gets the frequency range of the given frequency band, in milliHertz.
+     */
+    getBandFrequencyRange(uint16_t band)
+            generates (Result retval, uint32_t minFreqmHz, uint32_t maxFreqmHz);
+
+    /**
+     * Gets the band that has the most effect on the given frequency
+     * in milliHertz.
+     */
+    getBandForFrequency(uint32_t freqmHz)
+            generates (Result retval, uint16_t band);
+
+    /**
+     * Gets the names of all presets the equalizer supports.
+     */
+    getPresetNames() generates (Result retval, vec<string> names);
+
+    /**
+     * Sets the current preset using the index of the preset in the names
+     * vector returned via 'getPresetNames'.
+     */
+    setCurrentPreset(uint16_t preset) generates (Result retval);
+
+    /**
+     * Gets the current preset.
+     */
+    getCurrentPreset() generates (Result retval, uint16_t preset);
+
+    struct AllProperties {
+        uint16_t curPreset;
+        vec<int16_t> bandLevels;
+    };
+
+    /**
+     * Sets all properties at once.
+     */
+    setAllProperties(AllProperties properties) generates (Result retval);
+
+    /**
+     * Gets all properties at once.
+     */
+    getAllProperties() generates (Result retval, AllProperties properties);
+};
diff --git a/audio/effect/7.0/ILoudnessEnhancerEffect.hal b/audio/effect/7.0/ILoudnessEnhancerEffect.hal
new file mode 100644
index 0000000..0304f20
--- /dev/null
+++ b/audio/effect/7.0/ILoudnessEnhancerEffect.hal
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface ILoudnessEnhancerEffect extends IEffect {
+    /**
+     * Sets target gain expressed in millibels.
+     */
+    setTargetGain(int32_t targetGainMb) generates (Result retval);
+
+    /**
+     * Gets target gain expressed in millibels.
+     */
+    getTargetGain() generates (Result retval, int32_t targetGainMb);
+};
diff --git a/audio/effect/7.0/INoiseSuppressionEffect.hal b/audio/effect/7.0/INoiseSuppressionEffect.hal
new file mode 100644
index 0000000..2c6210c
--- /dev/null
+++ b/audio/effect/7.0/INoiseSuppressionEffect.hal
@@ -0,0 +1,68 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface INoiseSuppressionEffect extends IEffect {
+    enum Level : int32_t {
+        LOW,
+        MEDIUM,
+        HIGH
+    };
+
+    /**
+     * Sets suppression level.
+     */
+    setSuppressionLevel(Level level) generates (Result retval);
+
+    /**
+     * Gets suppression level.
+     */
+    getSuppressionLevel() generates (Result retval, Level level);
+
+    enum Type : int32_t {
+        SINGLE_CHANNEL,
+        MULTI_CHANNEL
+    };
+
+    /**
+     * Set suppression type.
+     */
+    setSuppressionType(Type type) generates (Result retval);
+
+    /**
+     * Get suppression type.
+     */
+    getSuppressionType() generates (Result retval, Type type);
+
+    struct AllProperties {
+        Level level;
+        Type type;
+    };
+
+    /**
+     * Sets all properties at once.
+     */
+    setAllProperties(AllProperties properties) generates (Result retval);
+
+    /**
+     * Gets all properties at once.
+     */
+    getAllProperties() generates (Result retval, AllProperties properties);
+};
diff --git a/audio/effect/7.0/IPresetReverbEffect.hal b/audio/effect/7.0/IPresetReverbEffect.hal
new file mode 100644
index 0000000..da61d24
--- /dev/null
+++ b/audio/effect/7.0/IPresetReverbEffect.hal
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IPresetReverbEffect extends IEffect {
+    enum Preset : int32_t {
+        NONE,        // no reverb or reflections
+        SMALLROOM,   // a small room less than five meters in length
+        MEDIUMROOM,  // a medium room with a length of ten meters or less
+        LARGEROOM,   // a large-sized room suitable for live performances
+        MEDIUMHALL,  // a medium-sized hall
+        LARGEHALL,   // a large-sized hall suitable for a full orchestra
+        PLATE,       // synthesis of the traditional plate reverb
+        LAST = PLATE
+    };
+
+    /**
+     * Sets the current preset.
+     */
+    setPreset(Preset preset) generates (Result retval);
+
+    /**
+     * Gets the current preset.
+     */
+    getPreset() generates (Result retval, Preset preset);
+};
diff --git a/audio/effect/7.0/IVirtualizerEffect.hal b/audio/effect/7.0/IVirtualizerEffect.hal
new file mode 100644
index 0000000..5d11435
--- /dev/null
+++ b/audio/effect/7.0/IVirtualizerEffect.hal
@@ -0,0 +1,92 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IVirtualizerEffect extends IEffect {
+    /**
+     * Returns whether setting virtualization strength is supported.
+     */
+    isStrengthSupported() generates (bool strengthSupported);
+
+    enum StrengthRange : uint16_t {
+        MIN = 0,
+        MAX = 1000
+    };
+
+    /**
+     * Sets virtualization strength.
+     *
+     * @param strength strength of the effect. The valid range for strength
+     *                 strength is [0, 1000], where 0 per mille designates the
+     *                 mildest effect and 1000 per mille designates the
+     *                 strongest.
+     * @return retval operation completion status.
+     */
+    setStrength(uint16_t strength) generates (Result retval);
+
+    /**
+     * Gets virtualization strength.
+     */
+    getStrength() generates (Result retval, uint16_t strength);
+
+    struct SpeakerAngles {
+        /** Speaker channel mask */
+        AudioChannelMask mask;
+        /**
+         * Horizontal speaker position angles for each channel ordered from LSb
+         * to MSb in the channel mask. The number of values is the number of
+         * channels in the channel mask.
+         *
+         * All angles are expressed in degrees and are relative to the listener.
+         *  - 0 is the direction the listener faces;
+         *  - 180 is behind the listener;
+         *  - -90 is to their left.
+         */
+        vec<int16_t> azimuth;
+        /**
+         * Vertical speaker position angles for each channel ordered from LSb
+         * to MSb in the channel mask. The number of values is the number of
+         * channels in the channel mask.
+         *
+         * All angles are expressed in degrees and are relative to the listener.
+         *  - 0 is the horizontal plane of the listener;
+         *  - +90 is above the listener;
+         *  - -90 is below the listener.
+         */
+        vec<int16_t> elevation;
+    };
+    /**
+     * Retrieves virtual speaker angles for the given channel mask on the
+     * specified device.
+     */
+    getVirtualSpeakerAngles(AudioChannelMask mask, DeviceAddress device)
+            generates (Result retval, SpeakerAngles speakerAngles);
+
+    /**
+     * Forces the virtualizer effect for the given output device.
+     */
+    forceVirtualizationMode(DeviceAddress device) generates (Result retval);
+
+    /**
+     * Returns audio device reflecting the current virtualization mode,
+     * Device type can be empty when not virtualizing.
+     */
+    getVirtualizationMode() generates (Result retval, DeviceAddress device);
+};
diff --git a/audio/effect/7.0/IVisualizerEffect.hal b/audio/effect/7.0/IVisualizerEffect.hal
new file mode 100644
index 0000000..b4e8659
--- /dev/null
+++ b/audio/effect/7.0/IVisualizerEffect.hal
@@ -0,0 +1,110 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import IEffect;
+
+interface IVisualizerEffect extends IEffect {
+    enum CaptureSizeRange : int32_t {
+        MAX = 1024,  // maximum capture size in samples
+        MIN = 128    // minimum capture size in samples
+    };
+
+    /**
+     * Sets the number PCM samples in the capture.
+     */
+    setCaptureSize(uint16_t captureSize) generates (Result retval);
+
+    /**
+     * Gets the number PCM samples in the capture.
+     */
+    getCaptureSize() generates (Result retval, uint16_t captureSize);
+
+    enum ScalingMode : int32_t {
+        // Keep in sync with SCALING_MODE_... in
+        // frameworks/base/media/java/android/media/audiofx/Visualizer.java
+        NORMALIZED = 0,
+        AS_PLAYED = 1
+    };
+
+    /**
+     * Specifies the way the captured data is scaled.
+     */
+    setScalingMode(ScalingMode scalingMode) generates (Result retval);
+
+    /**
+     * Retrieves the way the captured data is scaled.
+     */
+    getScalingMode() generates (Result retval, ScalingMode scalingMode);
+
+    /**
+     * Informs the visualizer about the downstream latency.
+     */
+    setLatency(uint32_t latencyMs) generates (Result retval);
+
+    /**
+     * Gets the downstream latency.
+     */
+    getLatency() generates (Result retval, uint32_t latencyMs);
+
+    enum MeasurementMode : int32_t {
+        // Keep in sync with MEASUREMENT_MODE_... in
+        // frameworks/base/media/java/android/media/audiofx/Visualizer.java
+        NONE = 0x0,
+        PEAK_RMS = 0x1
+    };
+
+    /**
+     * Specifies which measurements are to be made.
+     */
+    setMeasurementMode(MeasurementMode measurementMode)
+            generates (Result retval);
+
+    /**
+     * Retrieves which measurements are to be made.
+     */
+    getMeasurementMode() generates (
+            Result retval, MeasurementMode measurementMode);
+
+    /**
+     * Retrieves the latest PCM snapshot captured by the visualizer engine.  The
+     * number of samples to capture is specified by 'setCaptureSize' parameter.
+     *
+     * @return retval operation completion status.
+     * @return samples samples in 8 bit unsigned format (0 = 0x80)
+     */
+    capture() generates (Result retval, vec<uint8_t> samples);
+
+    struct Measurement {
+        MeasurementMode mode;    // discriminator
+        union Values {
+            struct PeakAndRms {
+                int32_t peakMb;  // millibels
+                int32_t rmsMb;   // millibels
+            } peakAndRms;
+        } value;
+    };
+    /**
+     * Retrieves the latest measurements. The measurements to be made
+     * are specified by 'setMeasurementMode' parameter.
+     *
+     * @return retval operation completion status.
+     * @return result measurement.
+     */
+    measure() generates (Result retval, Measurement result);
+};
diff --git a/audio/effect/7.0/types.hal b/audio/effect/7.0/types.hal
new file mode 100644
index 0000000..8f4f885
--- /dev/null
+++ b/audio/effect/7.0/types.hal
@@ -0,0 +1,304 @@
+/*
+ * 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.
+ */
+
+package android.hardware.audio.effect@7.0;
+
+import android.hardware.audio.common@7.0;
+import android.hidl.safe_union@1.0;
+
+enum Result : int32_t {
+    OK,
+    NOT_INITIALIZED,
+    INVALID_ARGUMENTS,
+    INVALID_STATE,
+    NOT_SUPPORTED,
+    RESULT_TOO_BIG
+};
+
+/**
+ * Effect engine capabilities/requirements flags.
+ *
+ * Definitions for flags field of effect descriptor.
+ *
+ * +----------------+--------+--------------------------------------------------
+ * | description    | bits   | values
+ * +----------------+--------+--------------------------------------------------
+ * | connection     | 0..2   | 0 insert: after track process
+ * | mode           |        | 1 auxiliary: connect to track auxiliary
+ * |                |        |  output and use send level
+ * |                |        | 2 replace: replaces track process function;
+ * |                |        |   must implement SRC, volume and mono to stereo.
+ * |                |        | 3 pre processing: applied below audio HAL on in
+ * |                |        | 4 post processing: applied below audio HAL on out
+ * |                |        | 5 - 7 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | insertion      | 3..5   | 0 none
+ * | preference     |        | 1 first of the chain
+ * |                |        | 2 last of the chain
+ * |                |        | 3 exclusive (only effect in the insert chain)
+ * |                |        | 4..7 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | Volume         | 6..8   | 0 none
+ * | management     |        | 1 implements volume control
+ * |                |        | 2 requires volume indication
+ * |                |        | 3 monitors requested volume
+ * |                |        | 4 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | Device         | 9..11  | 0 none
+ * | indication     |        | 1 requires device updates
+ * |                |        | 2, 4 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | Sample input   | 12..13 | 1 direct: process() function or
+ * | mode           |        |   EFFECT_CMD_SET_CONFIG command must specify
+ * |                |        |   a buffer descriptor
+ * |                |        | 2 provider: process() function uses the
+ * |                |        |   bufferProvider indicated by the
+ * |                |        |   EFFECT_CMD_SET_CONFIG command to request input.
+ * |                |        |   buffers.
+ * |                |        | 3 both: both input modes are supported
+ * +----------------+--------+--------------------------------------------------
+ * | Sample output  | 14..15 | 1 direct: process() function or
+ * | mode           |        |   EFFECT_CMD_SET_CONFIG command must specify
+ * |                |        |   a buffer descriptor
+ * |                |        | 2 provider: process() function uses the
+ * |                |        |   bufferProvider indicated by the
+ * |                |        |   EFFECT_CMD_SET_CONFIG command to request output
+ * |                |        |   buffers.
+ * |                |        | 3 both: both output modes are supported
+ * +----------------+--------+--------------------------------------------------
+ * | Hardware       | 16..17 | 0 No hardware acceleration
+ * | acceleration   |        | 1 non tunneled hw acceleration: the process()
+ * |                |        |   function reads the samples, send them to HW
+ * |                |        |   accelerated effect processor, reads back
+ * |                |        |   the processed samples and returns them
+ * |                |        |   to the output buffer.
+ * |                |        | 2 tunneled hw acceleration: the process()
+ * |                |        |   function is transparent. The effect interface
+ * |                |        |   is only used to control the effect engine.
+ * |                |        |   This mode is relevant for global effects
+ * |                |        |   actually applied by the audio hardware on
+ * |                |        |   the output stream.
+ * +----------------+--------+--------------------------------------------------
+ * | Audio Mode     | 18..19 | 0 none
+ * | indication     |        | 1 requires audio mode updates
+ * |                |        | 2..3 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | Audio source   | 20..21 | 0 none
+ * | indication     |        | 1 requires audio source updates
+ * |                |        | 2..3 reserved
+ * +----------------+--------+--------------------------------------------------
+ * | Effect offload | 22     | 0 The effect cannot be offloaded to an audio DSP
+ * | supported      |        | 1 The effect can be offloaded to an audio DSP
+ * +----------------+--------+--------------------------------------------------
+ * | Process        | 23     | 0 The effect implements a process function.
+ * | function       |        | 1 The effect does not implement a process
+ * | not            |        |   function: enabling the effect has no impact
+ * | implemented    |        |   on latency or CPU load.
+ * |                |        |   Effect implementations setting this flag do not
+ * |                |        |   have to implement a process function.
+ * +----------------+--------+--------------------------------------------------
+ */
+@export(name="", value_prefix="EFFECT_FLAG_")
+enum EffectFlags : int32_t {
+    // Insert mode
+    TYPE_SHIFT = 0,
+    TYPE_SIZE = 3,
+    TYPE_MASK = ((1 << TYPE_SIZE) -1) << TYPE_SHIFT,
+    TYPE_INSERT = 0 << TYPE_SHIFT,
+    TYPE_AUXILIARY = 1 << TYPE_SHIFT,
+    TYPE_REPLACE = 2 << TYPE_SHIFT,
+    TYPE_PRE_PROC = 3 << TYPE_SHIFT,
+    TYPE_POST_PROC = 4 << TYPE_SHIFT,
+
+    // Insert preference
+    INSERT_SHIFT = TYPE_SHIFT + TYPE_SIZE,
+    INSERT_SIZE = 3,
+    INSERT_MASK = ((1 << INSERT_SIZE) -1) << INSERT_SHIFT,
+    INSERT_ANY = 0 << INSERT_SHIFT,
+    INSERT_FIRST = 1 << INSERT_SHIFT,
+    INSERT_LAST = 2 << INSERT_SHIFT,
+    INSERT_EXCLUSIVE = 3 << INSERT_SHIFT,
+
+    // Volume control
+    VOLUME_SHIFT = INSERT_SHIFT + INSERT_SIZE,
+    VOLUME_SIZE = 3,
+    VOLUME_MASK = ((1 << VOLUME_SIZE) -1) << VOLUME_SHIFT,
+    VOLUME_CTRL = 1 << VOLUME_SHIFT,
+    VOLUME_IND = 2 << VOLUME_SHIFT,
+    VOLUME_MONITOR = 3 << VOLUME_SHIFT,
+    VOLUME_NONE = 0 << VOLUME_SHIFT,
+
+    // Device indication
+    DEVICE_SHIFT = VOLUME_SHIFT + VOLUME_SIZE,
+    DEVICE_SIZE = 3,
+    DEVICE_MASK = ((1 << DEVICE_SIZE) -1) << DEVICE_SHIFT,
+    DEVICE_IND = 1 << DEVICE_SHIFT,
+    DEVICE_NONE = 0 << DEVICE_SHIFT,
+
+    // Sample input modes
+    INPUT_SHIFT = DEVICE_SHIFT + DEVICE_SIZE,
+    INPUT_SIZE = 2,
+    INPUT_MASK = ((1 << INPUT_SIZE) -1) << INPUT_SHIFT,
+    INPUT_DIRECT = 1 << INPUT_SHIFT,
+    INPUT_PROVIDER = 2 << INPUT_SHIFT,
+    INPUT_BOTH = 3 << INPUT_SHIFT,
+
+    // Sample output modes
+    OUTPUT_SHIFT = INPUT_SHIFT + INPUT_SIZE,
+    OUTPUT_SIZE = 2,
+    OUTPUT_MASK = ((1 << OUTPUT_SIZE) -1) << OUTPUT_SHIFT,
+    OUTPUT_DIRECT = 1 << OUTPUT_SHIFT,
+    OUTPUT_PROVIDER = 2 << OUTPUT_SHIFT,
+    OUTPUT_BOTH = 3 << OUTPUT_SHIFT,
+
+    // Hardware acceleration mode
+    HW_ACC_SHIFT = OUTPUT_SHIFT + OUTPUT_SIZE,
+    HW_ACC_SIZE = 2,
+    HW_ACC_MASK = ((1 << HW_ACC_SIZE) -1) << HW_ACC_SHIFT,
+    HW_ACC_SIMPLE = 1 << HW_ACC_SHIFT,
+    HW_ACC_TUNNEL = 2 << HW_ACC_SHIFT,
+
+    // Audio mode indication
+    AUDIO_MODE_SHIFT = HW_ACC_SHIFT + HW_ACC_SIZE,
+    AUDIO_MODE_SIZE = 2,
+    AUDIO_MODE_MASK = ((1 << AUDIO_MODE_SIZE) -1) << AUDIO_MODE_SHIFT,
+    AUDIO_MODE_IND = 1 << AUDIO_MODE_SHIFT,
+    AUDIO_MODE_NONE = 0 << AUDIO_MODE_SHIFT,
+
+    // Audio source indication
+    AUDIO_SOURCE_SHIFT = AUDIO_MODE_SHIFT + AUDIO_MODE_SIZE,
+    AUDIO_SOURCE_SIZE = 2,
+    AUDIO_SOURCE_MASK = ((1 << AUDIO_SOURCE_SIZE) -1) << AUDIO_SOURCE_SHIFT,
+    AUDIO_SOURCE_IND = 1 << AUDIO_SOURCE_SHIFT,
+    AUDIO_SOURCE_NONE = 0 << AUDIO_SOURCE_SHIFT,
+
+    // Effect offload indication
+    OFFLOAD_SHIFT = AUDIO_SOURCE_SHIFT + AUDIO_SOURCE_SIZE,
+    OFFLOAD_SIZE = 1,
+    OFFLOAD_MASK = ((1 << OFFLOAD_SIZE) -1) << OFFLOAD_SHIFT,
+    OFFLOAD_SUPPORTED = 1 << OFFLOAD_SHIFT,
+
+    // Effect has no process indication
+    NO_PROCESS_SHIFT = OFFLOAD_SHIFT + OFFLOAD_SIZE,
+    NO_PROCESS_SIZE = 1,
+    NO_PROCESS_MASK = ((1 << NO_PROCESS_SIZE) -1) << NO_PROCESS_SHIFT,
+    NO_PROCESS = 1 << NO_PROCESS_SHIFT
+};
+
+/**
+ * The effect descriptor contains necessary information to facilitate the
+ * enumeration of the effect engines present in a library.
+ */
+struct EffectDescriptor {
+    /** UUID of to the OpenSL ES interface implemented by this effect. */
+    Uuid type;
+    /** UUID for this particular implementation. */
+    Uuid uuid;
+    /** Effect engine capabilities/requirements flags. */
+    bitfield<EffectFlags> flags;
+    /**
+     * CPU load indication expressed in 0.1 MIPS units as estimated on
+     * an ARM9E core (ARMv5TE) with 0 WS.
+     */
+    uint16_t cpuLoad;
+    /**
+     * Data memory usage expressed in KB and includes only dynamically
+     * allocated memory.
+     */
+    uint16_t memoryUsage;
+    /** Human readable effect name. */
+    string name;
+    /** Human readable effect implementor name. */
+    string implementor;
+};
+
+/**
+ * A buffer is a chunk of audio data for processing.  Multi-channel audio is
+ * always interleaved. The channel order is from LSB to MSB with regard to the
+ * channel mask definition in audio.h, audio_channel_mask_t, e.g.:
+ * Stereo: L, R; 5.1: FL, FR, FC, LFE, BL, BR.
+ *
+ * The buffer size is expressed in frame count, a frame being composed of
+ * samples for all channels at a given time. Frame size for unspecified format
+ * (AUDIO_FORMAT_OTHER) is 8 bit by definition.
+ */
+struct AudioBuffer {
+    uint64_t id;
+    uint32_t frameCount;
+    memory data;
+};
+
+@export(name="effect_buffer_access_e", value_prefix="EFFECT_BUFFER_")
+enum EffectBufferAccess : int32_t {
+    ACCESS_WRITE,
+    ACCESS_READ,
+    ACCESS_ACCUMULATE
+};
+
+/**
+ * The buffer config structure specifies the input or output audio format
+ * to be used by the effect engine.
+ */
+struct EffectBufferConfig {
+    safe_union OptionalBuffer {
+        Monostate unspecified;
+        AudioBuffer buf;
+    } buffer;
+    AudioConfigBaseOptional base;
+    safe_union OptionalAccessMode {
+        Monostate unspecified;
+        EffectBufferAccess value;
+    } accessMode;
+};
+
+struct EffectConfig {
+    EffectBufferConfig inputCfg;
+    EffectBufferConfig outputCfg;
+};
+
+@export(name="effect_feature_e", value_prefix="EFFECT_FEATURE_")
+enum EffectFeature : int32_t {
+    /** Supports auxiliary channels (e.g. dual mic noise suppressor). */
+    AUX_CHANNELS,
+    CNT
+};
+
+struct EffectAuxChannelsConfig {
+    /** Channel mask for main channels. */
+    AudioChannelMask mainChannels;
+    /** Channel mask for auxiliary channels. */
+    AudioChannelMask auxChannels;
+};
+
+struct EffectOffloadParameter {
+    /** True if the playback thread the effect is attached to is offloaded. */
+    bool isOffload;
+    /** I/O handle of the playback thread the effect is attached to. */
+    AudioIoHandle ioHandle;
+};
+
+/**
+ * The message queue flags used to synchronize reads and writes from
+ * the status message queue used by effects.
+ */
+enum MessageQueueFlagBits : uint32_t {
+    DONE_PROCESSING = 1 << 0,
+    REQUEST_PROCESS = 1 << 1,
+    REQUEST_PROCESS_REVERSE = 1 << 2,
+    REQUEST_QUIT = 1 << 3,
+    REQUEST_PROCESS_ALL =
+        REQUEST_PROCESS | REQUEST_PROCESS_REVERSE | REQUEST_QUIT
+};
diff --git a/audio/effect/7.0/xml/Android.bp b/audio/effect/7.0/xml/Android.bp
new file mode 100644
index 0000000..978e434
--- /dev/null
+++ b/audio/effect/7.0/xml/Android.bp
@@ -0,0 +1,14 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+xsd_config {
+    name: "audio_effects_conf_V7_0",
+    srcs: ["audio_effects_conf.xsd"],
+    package_name: "audio.effects.V7_0",
+}
diff --git a/audio/effect/7.0/xml/api/current.txt b/audio/effect/7.0/xml/api/current.txt
new file mode 100644
index 0000000..34cb541
--- /dev/null
+++ b/audio/effect/7.0/xml/api/current.txt
@@ -0,0 +1,208 @@
+// Signature format: 2.0
+package audio.effects.V7_0 {
+
+  public class AudioEffectsConf {
+    ctor public AudioEffectsConf();
+    method public audio.effects.V7_0.AudioEffectsConf.DeviceEffects getDeviceEffects();
+    method public audio.effects.V7_0.EffectsType getEffects();
+    method public audio.effects.V7_0.LibrariesType getLibraries();
+    method public audio.effects.V7_0.AudioEffectsConf.Postprocess getPostprocess();
+    method public audio.effects.V7_0.AudioEffectsConf.Preprocess getPreprocess();
+    method public audio.effects.V7_0.VersionType getVersion();
+    method public void setDeviceEffects(audio.effects.V7_0.AudioEffectsConf.DeviceEffects);
+    method public void setEffects(audio.effects.V7_0.EffectsType);
+    method public void setLibraries(audio.effects.V7_0.LibrariesType);
+    method public void setPostprocess(audio.effects.V7_0.AudioEffectsConf.Postprocess);
+    method public void setPreprocess(audio.effects.V7_0.AudioEffectsConf.Preprocess);
+    method public void setVersion(audio.effects.V7_0.VersionType);
+  }
+
+  public static class AudioEffectsConf.DeviceEffects {
+    ctor public AudioEffectsConf.DeviceEffects();
+    method public java.util.List<audio.effects.V7_0.DeviceProcessType> getDevicePort();
+  }
+
+  public static class AudioEffectsConf.Postprocess {
+    ctor public AudioEffectsConf.Postprocess();
+    method public java.util.List<audio.effects.V7_0.StreamPostprocessType> getStream();
+  }
+
+  public static class AudioEffectsConf.Preprocess {
+    ctor public AudioEffectsConf.Preprocess();
+    method public java.util.List<audio.effects.V7_0.StreamPreprocessType> getStream();
+  }
+
+  public class DeviceProcessType extends audio.effects.V7_0.StreamProcessingType {
+    ctor public DeviceProcessType();
+    method public String getAddress();
+    method public audio.effects.V7_0.DeviceType getType();
+    method public void setAddress(String);
+    method public void setType(audio.effects.V7_0.DeviceType);
+  }
+
+  public enum DeviceType {
+    method public String getRawName();
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_AUX_DIGITAL;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BACK_MIC;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_A2DP;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_BLE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BUILTIN_MIC;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_BUS;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_COMMUNICATION;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_ECHO_REFERENCE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_FM_TUNER;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_HDMI;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_HDMI_ARC;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_IP;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_LINE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_LOOPBACK;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_PROXY;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_REMOTE_SUBMIX;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_SPDIF;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_TELEPHONY_RX;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_TV_TUNER;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_USB_ACCESSORY;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_USB_DEVICE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_USB_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_VOICE_CALL;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_IN_WIRED_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_AUX_DIGITAL;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_AUX_LINE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_BUS;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_EARPIECE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_ECHO_CANCELLER;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_FM;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_HDMI;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_HDMI_ARC;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_HEARING_AID;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_IP;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_LINE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_PROXY;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_REMOTE_SUBMIX;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_SPDIF;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_TELEPHONY_TX;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_USB_ACCESSORY;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_USB_DEVICE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_USB_HEADSET;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADPHONE;
+    enum_constant public static final audio.effects.V7_0.DeviceType AUDIO_DEVICE_OUT_WIRED_HEADSET;
+  }
+
+  public class EffectImplType {
+    ctor public EffectImplType();
+    method public String getLibrary();
+    method public String getUuid();
+    method public void setLibrary(String);
+    method public void setUuid(String);
+  }
+
+  public class EffectProxyType extends audio.effects.V7_0.EffectType {
+    ctor public EffectProxyType();
+    method public audio.effects.V7_0.EffectImplType getLibhw();
+    method public audio.effects.V7_0.EffectImplType getLibsw();
+    method public void setLibhw(audio.effects.V7_0.EffectImplType);
+    method public void setLibsw(audio.effects.V7_0.EffectImplType);
+  }
+
+  public class EffectType extends audio.effects.V7_0.EffectImplType {
+    ctor public EffectType();
+    method public String getName();
+    method public void setName(String);
+  }
+
+  public class EffectsType {
+    ctor public EffectsType();
+    method public java.util.List<audio.effects.V7_0.EffectProxyType> getEffectProxy_optional();
+    method public java.util.List<audio.effects.V7_0.EffectType> getEffect_optional();
+  }
+
+  public class LibrariesType {
+    ctor public LibrariesType();
+    method public java.util.List<audio.effects.V7_0.LibrariesType.Library> getLibrary();
+  }
+
+  public static class LibrariesType.Library {
+    ctor public LibrariesType.Library();
+    method public String getName();
+    method public String getPath();
+    method public void setName(String);
+    method public void setPath(String);
+  }
+
+  public enum StreamInputType {
+    method public String getRawName();
+    enum_constant public static final audio.effects.V7_0.StreamInputType camcorder;
+    enum_constant public static final audio.effects.V7_0.StreamInputType echo_reference;
+    enum_constant public static final audio.effects.V7_0.StreamInputType fm_tuner;
+    enum_constant public static final audio.effects.V7_0.StreamInputType mic;
+    enum_constant public static final audio.effects.V7_0.StreamInputType unprocessed;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_call;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_communication;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_downlink;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_performance;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_recognition;
+    enum_constant public static final audio.effects.V7_0.StreamInputType voice_uplink;
+  }
+
+  public enum StreamOutputType {
+    method public String getRawName();
+    enum_constant public static final audio.effects.V7_0.StreamOutputType alarm;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType assistant;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType bluetooth_sco;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType dtmf;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType enforced_audible;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType music;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType notification;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType ring;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType system;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType tts;
+    enum_constant public static final audio.effects.V7_0.StreamOutputType voice_call;
+  }
+
+  public class StreamPostprocessType extends audio.effects.V7_0.StreamProcessingType {
+    ctor public StreamPostprocessType();
+    method public audio.effects.V7_0.StreamOutputType getType();
+    method public void setType(audio.effects.V7_0.StreamOutputType);
+  }
+
+  public class StreamPreprocessType extends audio.effects.V7_0.StreamProcessingType {
+    ctor public StreamPreprocessType();
+    method public audio.effects.V7_0.StreamInputType getType();
+    method public void setType(audio.effects.V7_0.StreamInputType);
+  }
+
+  public class StreamProcessingType {
+    ctor public StreamProcessingType();
+    method public java.util.List<audio.effects.V7_0.StreamProcessingType.Apply> getApply();
+  }
+
+  public static class StreamProcessingType.Apply {
+    ctor public StreamProcessingType.Apply();
+    method public String getEffect();
+    method public void setEffect(String);
+  }
+
+  public enum VersionType {
+    method public String getRawName();
+    enum_constant public static final audio.effects.V7_0.VersionType _2_0;
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method public static audio.effects.V7_0.AudioEffectsConf read(java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static String readText(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/tests/bar/1.0/.hidl_for_test b/audio/effect/7.0/xml/api/last_current.txt
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to audio/effect/7.0/xml/api/last_current.txt
diff --git a/tests/bar/1.0/.hidl_for_test b/audio/effect/7.0/xml/api/last_removed.txt
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to audio/effect/7.0/xml/api/last_removed.txt
diff --git a/audio/effect/7.0/xml/api/removed.txt b/audio/effect/7.0/xml/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/audio/effect/7.0/xml/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/audio/effect/7.0/xml/audio_effects_conf.xsd b/audio/effect/7.0/xml/audio_effects_conf.xsd
new file mode 100644
index 0000000..94f9f76
--- /dev/null
+++ b/audio/effect/7.0/xml/audio_effects_conf.xsd
@@ -0,0 +1,323 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
+           targetNamespace="http://schemas.android.com/audio/audio_effects_conf/v2_0"
+           xmlns:aec="http://schemas.android.com/audio/audio_effects_conf/v2_0"
+           elementFormDefault="qualified">
+  <!-- Simple types -->
+  <xs:simpleType name="versionType">
+    <xs:restriction base="xs:decimal">
+      <xs:enumeration value="2.0"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="uuidType">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="streamInputType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="mic"/>
+      <xs:enumeration value="voice_uplink"/>
+      <xs:enumeration value="voice_downlink"/>
+      <xs:enumeration value="voice_call"/>
+      <xs:enumeration value="camcorder"/>
+      <xs:enumeration value="voice_recognition"/>
+      <xs:enumeration value="voice_communication"/>
+      <xs:enumeration value="unprocessed"/>
+      <xs:enumeration value="voice_performance"/>
+      <xs:enumeration value="echo_reference"/>
+      <xs:enumeration value="fm_tuner"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="streamOutputType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="voice_call"/>
+      <xs:enumeration value="system"/>
+      <xs:enumeration value="ring"/>
+      <xs:enumeration value="music"/>
+      <xs:enumeration value="alarm"/>
+      <xs:enumeration value="notification"/>
+      <xs:enumeration value="bluetooth_sco"/>
+      <xs:enumeration value="enforced_audible"/>
+      <xs:enumeration value="dtmf"/>
+      <xs:enumeration value="tts"/>
+      <xs:enumeration value="assistant"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="relativePathType">
+    <xs:restriction base="xs:string">
+      <xs:pattern value="[^/].*"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <xs:simpleType name="deviceType">
+    <xs:restriction base="xs:string">
+      <xs:enumeration value="AUDIO_DEVICE_OUT_EARPIECE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_WIRED_HEADPHONE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_SCO_CARKIT"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_DIGITAL"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_USB_ACCESSORY"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_USB_DEVICE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_REMOTE_SUBMIX"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_TELEPHONY_TX"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_LINE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_HDMI_ARC"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_SPDIF"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_FM"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_AUX_LINE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_SPEAKER_SAFE"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_IP"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_BUS"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_PROXY"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_USB_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_HEARING_AID"/>
+      <xs:enumeration value="AUDIO_DEVICE_OUT_ECHO_CANCELLER"/>
+      <!-- Due to the xml format, IN types can not be a separated from OUT types -->
+      <xs:enumeration value="AUDIO_DEVICE_IN_COMMUNICATION"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BUILTIN_MIC"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_SCO_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_WIRED_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_AUX_DIGITAL"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_HDMI"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_VOICE_CALL"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_TELEPHONY_RX"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BACK_MIC"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_REMOTE_SUBMIX"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_DGTL_DOCK_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_USB_ACCESSORY"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_USB_DEVICE"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_FM_TUNER"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_TV_TUNER"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_LINE"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_SPDIF"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_A2DP"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_LOOPBACK"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_IP"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BUS"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_PROXY"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_USB_HEADSET"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_BLUETOOTH_BLE"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_HDMI_ARC"/>
+      <xs:enumeration value="AUDIO_DEVICE_IN_ECHO_REFERENCE"/>
+    </xs:restriction>
+  </xs:simpleType>
+  <!-- Complex types -->
+  <xs:complexType name="librariesType">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+        List of effect libraries to load. Each library element must have "name" and
+        "path" attributes. The latter is giving the path of the library .so file
+        relative to the standard effect folders: /(vendor|odm|system)/lib(64)?/soundfx/
+        Example for a library in "/vendor/lib/soundfx/lib.so":
+        <library name="name" path="lib.so"/>
+      </xs:documentation>
+    </xs:annotation>
+    <xs:sequence>
+      <xs:element name="library" minOccurs="0" maxOccurs="unbounded">
+        <xs:complexType>
+          <xs:attribute name="name" type="xs:string" use="required"/>
+          <xs:attribute name="path" type="aec:relativePathType" use="required"/>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="effectImplType">
+    <xs:attribute name="library" type="xs:string" use="required"/>
+    <xs:attribute name="uuid" type="aec:uuidType" use="required"/>
+  </xs:complexType>
+  <xs:complexType name="effectType">
+    <xs:complexContent>
+      <xs:extension base="aec:effectImplType">
+        <xs:attribute name="name" type="xs:string" use="required"/>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="effectProxyType">
+    <xs:complexContent>
+      <xs:extension base="aec:effectType">
+        <xs:sequence>
+          <xs:element name="libsw" type="aec:effectImplType"/>
+          <xs:element name="libhw" type="aec:effectImplType"/>
+        </xs:sequence>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="effectsType">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+        List of effects to load. Each effect element must contain "name",
+        "library", and "uuid" attrs. The value of the "library" attr must
+        correspond to the name of a "library" element. The name of the effect
+        element is indicative, only the value of the "uuid" element designates
+        the effect for the audio framework.  The uuid is the implementation
+        specific UUID as specified by the effect vendor. This is not the generic
+        effect type UUID.
+        For effect proxy implementations, SW and HW implementations of the effect
+        can be specified.
+        Example:
+        <effect name="name" library="lib" uuid="uuuu"/>
+        <effectProxy name="proxied" library="proxy" uuid="xxxx">
+            <libsw library="sw_bundle" uuid="yyyy"/>
+            <libhw library="offload_bundle" uuid="zzzz"/>
+        </effectProxy>
+      </xs:documentation>
+    </xs:annotation>
+    <xs:choice maxOccurs="unbounded">
+      <xs:element name="effect" type="aec:effectType" minOccurs="0" maxOccurs="unbounded"/>
+      <xs:element name="effectProxy" type="aec:effectProxyType" minOccurs="0" maxOccurs="unbounded"/>
+    </xs:choice>
+  </xs:complexType>
+  <xs:complexType name="streamProcessingType">
+    <xs:sequence>
+      <xs:element name="apply" minOccurs="0" maxOccurs="unbounded">
+        <xs:complexType>
+          <xs:attribute name="effect" type="xs:string" use="required"/>
+        </xs:complexType>
+      </xs:element>
+    </xs:sequence>
+  </xs:complexType>
+  <xs:complexType name="streamPreprocessType">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+        Audio preprocessing configuration. The processing configuration consists
+        of a list of elements each describing processing settings for a given
+        input stream. Valid input stream types are listed in "streamInputType".
+        Each stream element contains a list of "apply" elements. The value of the
+        "effect" attr must correspond to the name of an "effect" element.
+        Example:
+        <stream type="voice_communication">
+            <apply effect="effect1"/>
+            <apply effect="effect2"/>
+        </stream>
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexContent>
+      <xs:extension base="aec:streamProcessingType">
+        <xs:attribute name="type" type="aec:streamInputType" use="required"/>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="streamPostprocessType">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+        Audio postprocessing configuration. The processing configuration consists
+        of a list of elements each describing processing settings for a given
+        output stream. Valid output stream types are listed in "streamOutputType".
+        Each stream element contains a list of "apply" elements. The value of the
+        "effect" attr must correspond to the name of an "effect" element.
+        Example:
+        <stream type="music">
+            <apply effect="effect1"/>
+        </stream>
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexContent>
+      <xs:extension base="aec:streamProcessingType">
+        <xs:attribute name="type" type="aec:streamOutputType" use="required"/>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <xs:complexType name="deviceProcessType">
+    <xs:annotation>
+      <xs:documentation xml:lang="en">
+        Audio Device Effects configuration. The processing configuration consists
+        of a list of effects to be automatically added on a device Port when involved in an audio
+        patch.
+        Valid device type are listed in "deviceType" and shall be aligned.
+        Each stream element contains a list of "apply" elements. The value of the
+        "effect" attr must correspond to the name of an "effect" element.
+        Note that if the device is involved in a hardware patch, the effect must be hardware
+        accelerated.
+        Example:
+        <devicePort address="BUS00_USAGE_MAIN" type="AUDIO_DEVICE_OUT_BUS">
+            <apply effect="equalizer"/>
+            <apply effect="effect2"/>
+        </devicePort>
+      </xs:documentation>
+    </xs:annotation>
+    <xs:complexContent>
+      <xs:extension base="aec:streamProcessingType">
+         <xs:attribute name="address" type="xs:string" use="required"/>
+         <xs:attribute name="type" type="aec:deviceType" use="required"/>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
+  <!-- Root element -->
+  <xs:element name="audio_effects_conf">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element name="libraries" type="aec:librariesType"/>
+        <xs:element name="effects" type="aec:effectsType"/>
+        <xs:element name="postprocess" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="stream" type="aec:streamPostprocessType" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="preprocess" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="stream" type="aec:streamPreprocessType" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+        <xs:element name="deviceEffects" minOccurs="0" maxOccurs="1">
+          <xs:complexType>
+            <xs:sequence>
+              <xs:element name="devicePort" type="aec:deviceProcessType" minOccurs="0" maxOccurs="unbounded"/>
+            </xs:sequence>
+          </xs:complexType>
+        </xs:element>
+      </xs:sequence>
+      <xs:attribute name="version" type="aec:versionType" use="required"/>
+    </xs:complexType>
+    <!-- Keys and references -->
+    <xs:key name="libraryName">
+      <xs:selector xpath="aec:libraries/aec:library"/>
+      <xs:field xpath="@name"/>
+    </xs:key>
+    <xs:keyref name="libraryNameRef1" refer="aec:libraryName">
+      <xs:selector xpath="aec:effects/aec:effect"/>
+      <xs:field xpath="@library"/>
+    </xs:keyref>
+    <xs:keyref name="libraryNameRef2" refer="aec:libraryName">
+      <xs:selector xpath="aec:effects/aec:effect/aec:libsw"/>
+      <xs:field xpath="@library"/>
+    </xs:keyref>
+    <xs:keyref name="libraryNameRef3" refer="aec:libraryName">
+      <xs:selector xpath="aec:effects/aec:effect/aec:libhw"/>
+      <xs:field xpath="@library"/>
+    </xs:keyref>
+    <xs:key name="effectName">
+      <xs:selector xpath="aec:effects/aec:effect|aec:effects/aec:effectProxy"/>
+      <xs:field xpath="@name"/>
+    </xs:key>
+    <xs:keyref name="effectNamePreRef" refer="aec:effectName">
+      <xs:selector xpath="aec:preprocess/aec:stream/aec:apply"/>
+      <xs:field xpath="@effect"/>
+    </xs:keyref>
+    <xs:keyref name="effectNamePostRef" refer="aec:effectName">
+      <xs:selector xpath="aec:postprocess/aec:stream/aec:apply"/>
+      <xs:field xpath="@effect"/>
+    </xs:keyref>
+  </xs:element>
+</xs:schema>
diff --git a/audio/effect/all-versions/OWNERS b/audio/effect/all-versions/OWNERS
new file mode 100644
index 0000000..24071af
--- /dev/null
+++ b/audio/effect/all-versions/OWNERS
@@ -0,0 +1,2 @@
+elaurent@google.com
+mnaganov@google.com
diff --git a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
index 137ea24..c1a8b55 100644
--- a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
+++ b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.cpp
@@ -31,9 +31,7 @@
 namespace implementation {
 
 AcousticEchoCancelerEffect::AcousticEchoCancelerEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)) {}
-
-AcousticEchoCancelerEffect::~AcousticEchoCancelerEffect() {}
+    : mEffect(new Effect(true /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> AcousticEchoCancelerEffect::init() {
@@ -58,10 +56,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> AcousticEchoCancelerEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> AcousticEchoCancelerEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> AcousticEchoCancelerEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> AcousticEchoCancelerEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> AcousticEchoCancelerEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> AcousticEchoCancelerEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> AcousticEchoCancelerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                          setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -82,10 +102,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> AcousticEchoCancelerEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> AcousticEchoCancelerEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -108,10 +124,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> AcousticEchoCancelerEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> AcousticEchoCancelerEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
index 971f64d..d7a84f2 100644
--- a/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
+++ b/audio/effect/all-versions/default/AcousticEchoCancelerEffect.h
@@ -53,7 +53,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~AcousticEchoCancelerEffect();
+    virtual ~AcousticEchoCancelerEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/Android.bp b/audio/effect/all-versions/default/Android.bp
index d9bb78b..6df9dbf 100644
--- a/audio/effect/all-versions/default/Android.bp
+++ b/audio/effect/all-versions/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.audio.effect-impl_default",
     defaults: ["hidl_defaults"],
@@ -8,7 +17,6 @@
         "AudioBufferManager.cpp",
         "AutomaticGainControlEffect.cpp",
         "BassBoostEffect.cpp",
-        "Conversions.cpp",
         "DownmixEffect.cpp",
         "Effect.cpp",
         "EffectsFactory.cpp",
@@ -51,12 +59,13 @@
         "android.hardware.audio.common@2.0",
         "android.hardware.audio.common@2.0-util",
         "android.hardware.audio.effect@2.0",
+        "android.hardware.audio.effect@2.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=2",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_library_shared {
@@ -66,12 +75,13 @@
         "android.hardware.audio.common@4.0",
         "android.hardware.audio.common@4.0-util",
         "android.hardware.audio.effect@4.0",
+        "android.hardware.audio.effect@4.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=4",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_library_shared {
@@ -81,12 +91,13 @@
         "android.hardware.audio.common@5.0",
         "android.hardware.audio.common@5.0-util",
         "android.hardware.audio.effect@5.0",
+        "android.hardware.audio.effect@5.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=5",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_library_shared {
@@ -96,10 +107,27 @@
         "android.hardware.audio.common@6.0",
         "android.hardware.audio.common@6.0-util",
         "android.hardware.audio.effect@6.0",
+        "android.hardware.audio.effect@6.0-util",
     ],
     cflags: [
         "-DMAJOR_VERSION=6",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@7.0-impl",
+    defaults: ["android.hardware.audio.effect-impl_default"],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.effect@7.0",
+        "android.hardware.audio.effect@7.0-util",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
 }
diff --git a/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp b/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
index 655a4cd..110b1b6 100644
--- a/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
+++ b/audio/effect/all-versions/default/AutomaticGainControlEffect.cpp
@@ -30,9 +30,7 @@
 namespace implementation {
 
 AutomaticGainControlEffect::AutomaticGainControlEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)) {}
-
-AutomaticGainControlEffect::~AutomaticGainControlEffect() {}
+    : mEffect(new Effect(true /*isInput*/, handle)) {}
 
 void AutomaticGainControlEffect::propertiesFromHal(
     const t_agc_settings& halProperties, IAutomaticGainControlEffect::AllProperties* properties) {
@@ -71,10 +69,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> AutomaticGainControlEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> AutomaticGainControlEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> AutomaticGainControlEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> AutomaticGainControlEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> AutomaticGainControlEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> AutomaticGainControlEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> AutomaticGainControlEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                          setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -95,10 +115,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> AutomaticGainControlEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> AutomaticGainControlEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -121,10 +137,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> AutomaticGainControlEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> AutomaticGainControlEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/AutomaticGainControlEffect.h b/audio/effect/all-versions/default/AutomaticGainControlEffect.h
index 67e260a..f30d7a5 100644
--- a/audio/effect/all-versions/default/AutomaticGainControlEffect.h
+++ b/audio/effect/all-versions/default/AutomaticGainControlEffect.h
@@ -55,7 +55,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -107,7 +113,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~AutomaticGainControlEffect();
+    virtual ~AutomaticGainControlEffect() = default;
 
     void propertiesFromHal(const t_agc_settings& halProperties,
                            IAutomaticGainControlEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/BassBoostEffect.cpp b/audio/effect/all-versions/default/BassBoostEffect.cpp
index 04fd486..33fea3b 100644
--- a/audio/effect/all-versions/default/BassBoostEffect.cpp
+++ b/audio/effect/all-versions/default/BassBoostEffect.cpp
@@ -30,9 +30,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-BassBoostEffect::BassBoostEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-BassBoostEffect::~BassBoostEffect() {}
+BassBoostEffect::BassBoostEffect(effect_handle_t handle)
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> BassBoostEffect::init() {
@@ -57,10 +56,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> BassBoostEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> BassBoostEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> BassBoostEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> BassBoostEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> BassBoostEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> BassBoostEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> BassBoostEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                               setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> BassBoostEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> BassBoostEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -105,10 +122,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> BassBoostEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> BassBoostEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/BassBoostEffect.h b/audio/effect/all-versions/default/BassBoostEffect.h
index b89bb22..48f586c 100644
--- a/audio/effect/all-versions/default/BassBoostEffect.h
+++ b/audio/effect/all-versions/default/BassBoostEffect.h
@@ -55,7 +55,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -100,7 +106,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~BassBoostEffect();
+    virtual ~BassBoostEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/Conversions.cpp b/audio/effect/all-versions/default/Conversions.cpp
deleted file mode 100644
index b1c0b0d..0000000
--- a/audio/effect/all-versions/default/Conversions.cpp
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Copyright (C) 2018 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 "Conversions.h"
-#include "HidlUtils.h"
-
-#include <memory.h>
-#include <stdio.h>
-
-#include <common/all-versions/VersionUtils.h>
-
-using ::android::hardware::audio::common::utils::EnumBitfield;
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace effect {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
-
-void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
-                             EffectDescriptor* descriptor) {
-    HidlUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
-    HidlUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
-    descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
-    descriptor->cpuLoad = halDescriptor.cpuLoad;
-    descriptor->memoryUsage = halDescriptor.memoryUsage;
-    memcpy(descriptor->name.data(), halDescriptor.name, descriptor->name.size());
-    memcpy(descriptor->implementor.data(), halDescriptor.implementor,
-           descriptor->implementor.size());
-}
-
-std::string uuidToString(const effect_uuid_t& halUuid) {
-    char str[64];
-    snprintf(str, sizeof(str), "%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", halUuid.timeLow,
-             halUuid.timeMid, halUuid.timeHiAndVersion, halUuid.clockSeq, halUuid.node[0],
-             halUuid.node[1], halUuid.node[2], halUuid.node[3], halUuid.node[4], halUuid.node[5]);
-    return str;
-}
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace effect
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
diff --git a/audio/effect/all-versions/default/Conversions.h b/audio/effect/all-versions/default/Conversions.h
deleted file mode 100644
index 75aab24..0000000
--- a/audio/effect/all-versions/default/Conversions.h
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2018 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_AUDIO_EFFECT_CONVERSIONS_H_
-#define ANDROID_HARDWARE_AUDIO_EFFECT_CONVERSIONS_H_
-
-#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
-
-#include <string>
-
-#include <system/audio_effect.h>
-
-namespace android {
-namespace hardware {
-namespace audio {
-namespace effect {
-namespace CPP_VERSION {
-namespace implementation {
-
-using ::android::hardware::audio::effect::CPP_VERSION::EffectDescriptor;
-
-void effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
-                             EffectDescriptor* descriptor);
-std::string uuidToString(const effect_uuid_t& halUuid);
-
-}  // namespace implementation
-}  // namespace CPP_VERSION
-}  // namespace effect
-}  // namespace audio
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_AUDIO_EFFECT_CONVERSIONS_H_
diff --git a/audio/effect/all-versions/default/DownmixEffect.cpp b/audio/effect/all-versions/default/DownmixEffect.cpp
index c001a5f..f324cff 100644
--- a/audio/effect/all-versions/default/DownmixEffect.cpp
+++ b/audio/effect/all-versions/default/DownmixEffect.cpp
@@ -30,9 +30,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-DownmixEffect::DownmixEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-DownmixEffect::~DownmixEffect() {}
+DownmixEffect::DownmixEffect(effect_handle_t handle)
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> DownmixEffect::init() {
@@ -57,10 +56,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> DownmixEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> DownmixEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> DownmixEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> DownmixEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> DownmixEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> DownmixEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> DownmixEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                             setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> DownmixEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> DownmixEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -105,10 +122,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> DownmixEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> DownmixEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/DownmixEffect.h b/audio/effect/all-versions/default/DownmixEffect.h
index 40e462e..caacd06 100644
--- a/audio/effect/all-versions/default/DownmixEffect.h
+++ b/audio/effect/all-versions/default/DownmixEffect.h
@@ -53,7 +53,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -97,7 +103,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~DownmixEffect();
+    virtual ~DownmixEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/Effect.cpp b/audio/effect/all-versions/default/Effect.cpp
index 406a571..ccfc6b2 100644
--- a/audio/effect/all-versions/default/Effect.cpp
+++ b/audio/effect/all-versions/default/Effect.cpp
@@ -19,7 +19,6 @@
 #define LOG_TAG "EffectHAL"
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
-#include "Conversions.h"
 #include "Effect.h"
 #include "common/all-versions/default/EffectMap.h"
 
@@ -27,8 +26,10 @@
 
 #define ATRACE_TAG ATRACE_TAG_AUDIO
 
+#include <HidlUtils.h>
 #include <android/log.h>
 #include <media/EffectsFactoryApi.h>
+#include <util/EffectUtils.h>
 #include <utils/Trace.h>
 
 #include "VersionUtils.h"
@@ -40,7 +41,10 @@
 namespace CPP_VERSION {
 namespace implementation {
 
+#if MAJOR_VERSION <= 6
 using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
 
 namespace {
 
@@ -136,9 +140,12 @@
 const char* Effect::sContextResultOfCommand = "returned status";
 const char* Effect::sContextCallToCommand = "error";
 const char* Effect::sContextCallFunction = sContextCallToCommand;
+const char* Effect::sContextConversion = "conversion";
 
-Effect::Effect(effect_handle_t handle)
-    : mHandle(handle), mEfGroup(nullptr), mStopProcessThread(false) {}
+Effect::Effect(bool isInput, effect_handle_t handle)
+    : mIsInput(isInput), mHandle(handle), mEfGroup(nullptr), mStopProcessThread(false) {
+    (void)mIsInput;  // prevent 'unused field' warnings in pre-V7 versions.
+}
 
 Effect::~Effect() {
     ATRACE_CALL();
@@ -180,7 +187,8 @@
     return halData;
 }
 
-// static
+#if MAJOR_VERSION <= 6
+
 void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
                                             EffectAuxChannelsConfig* config) {
     config->mainChannels = AudioChannelBitfield(halConfig.main_channels);
@@ -194,46 +202,24 @@
     halConfig->aux_channels = static_cast<audio_channel_mask_t>(config.auxChannels);
 }
 
-// static
-void Effect::effectBufferConfigFromHal(const buffer_config_t& halConfig,
-                                       EffectBufferConfig* config) {
-    config->buffer.id = 0;
-    config->buffer.frameCount = 0;
-    config->samplingRateHz = halConfig.samplingRate;
-    config->channels = AudioChannelBitfield(halConfig.channels);
-    config->format = AudioFormat(halConfig.format);
-    config->accessMode = EffectBufferAccess(halConfig.accessMode);
-    config->mask = static_cast<decltype(config->mask)>(halConfig.mask);
+#else  // MAJOR_VERSION <= 6
+
+void Effect::effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
+                                            EffectAuxChannelsConfig* config) {
+    (void)HidlUtils::audioChannelMaskFromHal(halConfig.main_channels, mIsInput,
+                                             &config->mainChannels);
+    (void)HidlUtils::audioChannelMaskFromHal(halConfig.aux_channels, mIsInput,
+                                             &config->auxChannels);
 }
 
 // static
-void Effect::effectBufferConfigToHal(const EffectBufferConfig& config, buffer_config_t* halConfig) {
-    // Note: setting the buffers directly is considered obsolete. They need to be set
-    // using 'setProcessBuffers'.
-    halConfig->buffer.frameCount = 0;
-    halConfig->buffer.raw = NULL;
-    halConfig->samplingRate = config.samplingRateHz;
-    halConfig->channels = static_cast<uint32_t>(config.channels);
-    // Note: The framework code does not use BP.
-    halConfig->bufferProvider.cookie = NULL;
-    halConfig->bufferProvider.getBuffer = NULL;
-    halConfig->bufferProvider.releaseBuffer = NULL;
-    halConfig->format = static_cast<uint8_t>(config.format);
-    halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
-    halConfig->mask = static_cast<uint8_t>(config.mask);
+void Effect::effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
+                                          channel_config_t* halConfig) {
+    (void)HidlUtils::audioChannelMaskToHal(config.mainChannels, &halConfig->main_channels);
+    (void)HidlUtils::audioChannelMaskToHal(config.auxChannels, &halConfig->aux_channels);
 }
 
-// static
-void Effect::effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config) {
-    effectBufferConfigFromHal(halConfig.inputCfg, &config->inputCfg);
-    effectBufferConfigFromHal(halConfig.outputCfg, &config->outputCfg);
-}
-
-// static
-void Effect::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) {
-    effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg);
-    effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg);
-}
+#endif  // MAJOR_VERSION <= 6
 
 // static
 void Effect::effectOffloadParamToHal(const EffectOffloadParameter& offload,
@@ -299,7 +285,7 @@
         (*mHandle)->command(mHandle, commandCode, 0, NULL, &halResultSize, &halConfig);
     EffectConfig config;
     if (status == OK) {
-        effectConfigFromHal(halConfig, &config);
+        status = EffectUtils::effectConfigFromHal(halConfig, mIsInput, &config);
     }
     cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config);
 }
@@ -464,7 +450,7 @@
                              const sp<IEffectBufferProviderCallback>& inputBufferProvider,
                              const sp<IEffectBufferProviderCallback>& outputBufferProvider) {
     effect_config_t halConfig;
-    effectConfigToHal(config, &halConfig);
+    EffectUtils::effectConfigToHal(config, &halConfig);
     if (inputBufferProvider != 0) {
         LOG_FATAL("Using input buffer provider is not supported");
     }
@@ -507,11 +493,65 @@
     return sendCommandReturningStatus(EFFECT_CMD_DISABLE, "DISABLE");
 }
 
+Return<Result> Effect::setAudioSource(
+#if MAJOR_VERSION <= 6
+        AudioSource source
+#else
+        const AudioSource& source
+#endif
+) {
+    audio_source_t halSource;
+    if (status_t status = HidlUtils::audioSourceToHal(source, &halSource); status == NO_ERROR) {
+        uint32_t halSourceParam = static_cast<uint32_t>(halSource);
+        return sendCommand(EFFECT_CMD_SET_AUDIO_SOURCE, "SET_AUDIO_SOURCE", sizeof(uint32_t),
+                           &halSourceParam);
+    } else {
+        return analyzeStatus(__func__, "audioSourceToHal", sContextConversion, status);
+    }
+}
+
+#if MAJOR_VERSION <= 6
+
 Return<Result> Effect::setDevice(AudioDeviceBitfield device) {
     uint32_t halDevice = static_cast<uint32_t>(device);
     return sendCommand(EFFECT_CMD_SET_DEVICE, "SET_DEVICE", sizeof(uint32_t), &halDevice);
 }
 
+Return<Result> Effect::setInputDevice(AudioDeviceBitfield device) {
+    uint32_t halDevice = static_cast<uint32_t>(device);
+    return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
+                       &halDevice);
+}
+
+#else  // MAJOR_VERSION <= 6
+
+Return<Result> Effect::setDevice(const DeviceAddress& device) {
+    audio_devices_t halDevice;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (status_t status = HidlUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress);
+        status == NO_ERROR) {
+        uint32_t halDeviceParam = static_cast<uint32_t>(halDevice);
+        return sendCommand(EFFECT_CMD_SET_DEVICE, "SET_DEVICE", sizeof(uint32_t), &halDeviceParam);
+    } else {
+        return analyzeStatus(__func__, "deviceAddressToHal", sContextConversion, status);
+    }
+}
+
+Return<Result> Effect::setInputDevice(const DeviceAddress& device) {
+    audio_devices_t halDevice;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (status_t status = HidlUtils::deviceAddressToHal(device, &halDevice, halDeviceAddress);
+        status == NO_ERROR) {
+        uint32_t halDeviceParam = static_cast<uint32_t>(halDevice);
+        return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
+                           &halDeviceParam);
+    } else {
+        return analyzeStatus(__func__, "deviceAddressToHal", sContextConversion, status);
+    }
+}
+
+#endif  // MAJOR_VERSION <= 6
+
 Return<void> Effect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                      setAndGetVolume_cb _hidl_cb) {
     uint32_t halDataSize;
@@ -546,12 +586,6 @@
                          inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> Effect::setInputDevice(AudioDeviceBitfield device) {
-    uint32_t halDevice = static_cast<uint32_t>(device);
-    return sendCommand(EFFECT_CMD_SET_INPUT_DEVICE, "SET_INPUT_DEVICE", sizeof(uint32_t),
-                       &halDevice);
-}
-
 Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
     getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb);
     return Void();
@@ -598,12 +632,6 @@
                                       "SET_FEATURE_CONFIG AUX_CHANNELS", halCmd.size(), &halCmd[0]);
 }
 
-Return<Result> Effect::setAudioSource(AudioSource source) {
-    uint32_t halSource = static_cast<uint32_t>(source);
-    return sendCommand(EFFECT_CMD_SET_AUDIO_SOURCE, "SET_AUDIO_SOURCE", sizeof(uint32_t),
-                       &halSource);
-}
-
 Return<Result> Effect::offload(const EffectOffloadParameter& param) {
     effect_offload_param_t halParam;
     effectOffloadParamToHal(param, &halParam);
@@ -617,7 +645,7 @@
     status_t status = (*mHandle)->get_descriptor(mHandle, &halDescriptor);
     EffectDescriptor descriptor;
     if (status == OK) {
-        effectDescriptorFromHal(halDescriptor, &descriptor);
+        status = EffectUtils::effectDescriptorFromHal(halDescriptor, &descriptor);
     }
     _hidl_cb(analyzeStatus("get_descriptor", "", sContextCallFunction, status), descriptor);
     return Void();
diff --git a/audio/effect/all-versions/default/Effect.h b/audio/effect/all-versions/default/Effect.h
index 181e542..d5218f7 100644
--- a/audio/effect/all-versions/default/Effect.h
+++ b/audio/effect/all-versions/default/Effect.h
@@ -47,7 +47,9 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
 using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioDeviceBitfield;
+#endif
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::effect::CPP_VERSION;
 
@@ -56,7 +58,7 @@
     using GetParameterSuccessCallback =
         std::function<void(uint32_t valueSize, const void* valueData)>;
 
-    explicit Effect(effect_handle_t handle);
+    Effect(bool isInput, effect_handle_t handle);
 
     // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
     Return<Result> init() override;
@@ -66,7 +68,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -74,14 +84,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -104,6 +112,10 @@
     Return<void> debug(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
 
     // Utility methods for extending interfaces.
+    static const char* sContextConversion;
+
+    Result analyzeStatus(const char* funcName, const char* subFuncName,
+                         const char* contextDescription, status_t status);
     template <typename T>
     Return<void> getIntegerParam(uint32_t paramId,
                                  std::function<void(Result retval, T paramValue)> cb) {
@@ -170,6 +182,7 @@
     static const char* sContextCallToCommand;
     static const char* sContextCallFunction;
 
+    const bool mIsInput;
     effect_handle_t mHandle;
     sp<AudioBufferWrapper> mInBuffer;
     sp<AudioBufferWrapper> mOutBuffer;
@@ -186,24 +199,16 @@
     static size_t alignedSizeIn(size_t s);
     template <typename T>
     std::unique_ptr<uint8_t[]> hidlVecToHal(const hidl_vec<T>& vec, uint32_t* halDataSize);
-    static void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
-                                               EffectAuxChannelsConfig* config);
+    void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
+                                        EffectAuxChannelsConfig* config);
     static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
                                              channel_config_t* halConfig);
-    static void effectBufferConfigFromHal(const buffer_config_t& halConfig,
-                                          EffectBufferConfig* config);
-    static void effectBufferConfigToHal(const EffectBufferConfig& config,
-                                        buffer_config_t* halConfig);
-    static void effectConfigFromHal(const effect_config_t& halConfig, EffectConfig* config);
-    static void effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
     static void effectOffloadParamToHal(const EffectOffloadParameter& offload,
                                         effect_offload_param_t* halOffload);
     static std::vector<uint8_t> parameterToHal(uint32_t paramSize, const void* paramData,
                                                uint32_t valueSize, const void** valueData);
 
     Result analyzeCommandStatus(const char* commandName, const char* context, status_t status);
-    Result analyzeStatus(const char* funcName, const char* subFuncName,
-                         const char* contextDescription, status_t status);
     void getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb);
     Result getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
                                 GetCurrentConfigSuccessCallback onSuccess);
diff --git a/audio/effect/all-versions/default/EffectsFactory.cpp b/audio/effect/all-versions/default/EffectsFactory.cpp
index acce7de..eb1cb49 100644
--- a/audio/effect/all-versions/default/EffectsFactory.cpp
+++ b/audio/effect/all-versions/default/EffectsFactory.cpp
@@ -19,12 +19,10 @@
 #include "AcousticEchoCancelerEffect.h"
 #include "AutomaticGainControlEffect.h"
 #include "BassBoostEffect.h"
-#include "Conversions.h"
 #include "DownmixEffect.h"
 #include "Effect.h"
 #include "EnvironmentalReverbEffect.h"
 #include "EqualizerEffect.h"
-#include "HidlUtils.h"
 #include "LoudnessEnhancerEffect.h"
 #include "NoiseSuppressionEffect.h"
 #include "PresetReverbEffect.h"
@@ -32,6 +30,7 @@
 #include "VisualizerEffect.h"
 #include "common/all-versions/default/EffectMap.h"
 
+#include <UuidUtils.h>
 #include <android/log.h>
 #include <media/EffectsFactoryApi.h>
 #include <system/audio_effects/effect_aec.h>
@@ -45,6 +44,7 @@
 #include <system/audio_effects/effect_presetreverb.h>
 #include <system/audio_effects/effect_virtualizer.h>
 #include <system/audio_effects/effect_visualizer.h>
+#include <util/EffectUtils.h>
 
 namespace android {
 namespace hardware {
@@ -53,7 +53,7 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
 
 // static
 sp<IEffect> EffectsFactory::dispatchEffectInstanceCreation(const effect_descriptor_t& halDescriptor,
@@ -82,7 +82,9 @@
     } else if (memcmp(halUuid, SL_IID_VISUALIZATION, sizeof(effect_uuid_t)) == 0) {
         return new VisualizerEffect(handle);
     }
-    return new Effect(handle);
+    const bool isInput =
+            (halDescriptor.flags & EFFECT_FLAG_TYPE_PRE_PROC) == EFFECT_FLAG_TYPE_PRE_PROC;
+    return new Effect(isInput, handle);
 }
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffectsFactory follow.
@@ -105,7 +107,7 @@
         effect_descriptor_t halDescriptor;
         status = EffectQueryEffect(i, &halDescriptor);
         if (status == OK) {
-            effectDescriptorFromHal(halDescriptor, &result[i]);
+            EffectUtils::effectDescriptorFromHal(halDescriptor, &result[i]);
         } else {
             ALOGE("Error querying effect at position %d / %d: %s", i, numEffects,
                   strerror(-status));
@@ -135,15 +137,15 @@
 
 Return<void> EffectsFactory::getDescriptor(const Uuid& uuid, getDescriptor_cb _hidl_cb) {
     effect_uuid_t halUuid;
-    HidlUtils::uuidToHal(uuid, &halUuid);
+    UuidUtils::uuidToHal(uuid, &halUuid);
     effect_descriptor_t halDescriptor;
     status_t status = EffectGetDescriptor(&halUuid, &halDescriptor);
     EffectDescriptor descriptor;
-    effectDescriptorFromHal(halDescriptor, &descriptor);
+    EffectUtils::effectDescriptorFromHal(halDescriptor, &descriptor);
     Result retval(Result::OK);
     if (status != OK) {
-        ALOGE("Error querying effect descriptor for %s: %s", uuidToString(halUuid).c_str(),
-              strerror(-status));
+        ALOGE("Error querying effect descriptor for %s: %s",
+              UuidUtils::uuidToString(halUuid).c_str(), strerror(-status));
         if (status == -ENOENT) {
             retval = Result::INVALID_ARGUMENTS;
         } else {
@@ -170,7 +172,7 @@
 Return<void> EffectsFactory::createEffectImpl(const Uuid& uuid, int32_t session, int32_t ioHandle,
                                               int32_t device, createEffect_cb _hidl_cb) {
     effect_uuid_t halUuid;
-    HidlUtils::uuidToHal(uuid, &halUuid);
+    UuidUtils::uuidToHal(uuid, &halUuid);
     effect_handle_t handle;
     Result retval(Result::OK);
     status_t status;
@@ -189,13 +191,14 @@
             effect = dispatchEffectInstanceCreation(halDescriptor, handle);
             effectId = EffectMap::getInstance().add(handle);
         } else {
-            ALOGE("Error querying effect descriptor for %s: %s", uuidToString(halUuid).c_str(),
-                  strerror(-status));
+            ALOGE("Error querying effect descriptor for %s: %s",
+                  UuidUtils::uuidToString(halUuid).c_str(), strerror(-status));
             EffectRelease(handle);
         }
     }
     if (status != OK) {
-        ALOGE("Error creating effect %s: %s", uuidToString(halUuid).c_str(), strerror(-status));
+        ALOGE("Error creating effect %s: %s", UuidUtils::uuidToString(halUuid).c_str(),
+              strerror(-status));
         if (status == -ENOENT) {
             retval = Result::INVALID_ARGUMENTS;
         } else {
diff --git a/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp b/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
index 78122d4..e95a267 100644
--- a/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
+++ b/audio/effect/all-versions/default/EnvironmentalReverbEffect.cpp
@@ -31,9 +31,7 @@
 namespace implementation {
 
 EnvironmentalReverbEffect::EnvironmentalReverbEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)) {}
-
-EnvironmentalReverbEffect::~EnvironmentalReverbEffect() {}
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 void EnvironmentalReverbEffect::propertiesFromHal(
     const t_reverb_settings& halProperties, IEnvironmentalReverbEffect::AllProperties* properties) {
@@ -86,10 +84,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> EnvironmentalReverbEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> EnvironmentalReverbEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> EnvironmentalReverbEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> EnvironmentalReverbEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> EnvironmentalReverbEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> EnvironmentalReverbEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> EnvironmentalReverbEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                         setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -110,10 +130,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> EnvironmentalReverbEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> EnvironmentalReverbEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -136,10 +152,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> EnvironmentalReverbEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> EnvironmentalReverbEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/EnvironmentalReverbEffect.h b/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
index bb422d4..9694b5d 100644
--- a/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
+++ b/audio/effect/all-versions/default/EnvironmentalReverbEffect.h
@@ -57,7 +57,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -65,14 +73,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -125,7 +131,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~EnvironmentalReverbEffect();
+    virtual ~EnvironmentalReverbEffect() = default;
 
     void propertiesFromHal(const t_reverb_settings& halProperties,
                            IEnvironmentalReverbEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/EqualizerEffect.cpp b/audio/effect/all-versions/default/EqualizerEffect.cpp
index 1b983ec..fffe8cd 100644
--- a/audio/effect/all-versions/default/EqualizerEffect.cpp
+++ b/audio/effect/all-versions/default/EqualizerEffect.cpp
@@ -31,9 +31,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-EqualizerEffect::EqualizerEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-EqualizerEffect::~EqualizerEffect() {}
+EqualizerEffect::EqualizerEffect(effect_handle_t handle)
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 void EqualizerEffect::propertiesFromHal(const t_equalizer_settings& halProperties,
                                         IEqualizerEffect::AllProperties* properties) {
@@ -80,10 +79,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> EqualizerEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> EqualizerEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> EqualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> EqualizerEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> EqualizerEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> EqualizerEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> EqualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                               setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -103,10 +124,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> EqualizerEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> EqualizerEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -128,10 +145,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> EqualizerEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> EqualizerEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/EqualizerEffect.h b/audio/effect/all-versions/default/EqualizerEffect.h
index b1cbefd..7a6bc0a 100644
--- a/audio/effect/all-versions/default/EqualizerEffect.h
+++ b/audio/effect/all-versions/default/EqualizerEffect.h
@@ -57,7 +57,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -65,14 +73,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -111,7 +117,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~EqualizerEffect();
+    virtual ~EqualizerEffect() = default;
 
     void propertiesFromHal(const t_equalizer_settings& halProperties,
                            IEqualizerEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp b/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
index ebd5197..c7add86 100644
--- a/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
+++ b/audio/effect/all-versions/default/LoudnessEnhancerEffect.cpp
@@ -33,9 +33,7 @@
 namespace implementation {
 
 LoudnessEnhancerEffect::LoudnessEnhancerEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)) {}
-
-LoudnessEnhancerEffect::~LoudnessEnhancerEffect() {}
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> LoudnessEnhancerEffect::init() {
@@ -60,10 +58,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> LoudnessEnhancerEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> LoudnessEnhancerEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> LoudnessEnhancerEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> LoudnessEnhancerEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> LoudnessEnhancerEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> LoudnessEnhancerEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> LoudnessEnhancerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                      setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -83,10 +103,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> LoudnessEnhancerEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> LoudnessEnhancerEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -108,10 +124,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> LoudnessEnhancerEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> LoudnessEnhancerEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/LoudnessEnhancerEffect.h b/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
index 8baf128..6d80207 100644
--- a/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
+++ b/audio/effect/all-versions/default/LoudnessEnhancerEffect.h
@@ -53,7 +53,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~LoudnessEnhancerEffect();
+    virtual ~LoudnessEnhancerEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp b/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
index d01bbe5..9e75237 100644
--- a/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
+++ b/audio/effect/all-versions/default/NoiseSuppressionEffect.cpp
@@ -30,9 +30,7 @@
 namespace implementation {
 
 NoiseSuppressionEffect::NoiseSuppressionEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)) {}
-
-NoiseSuppressionEffect::~NoiseSuppressionEffect() {}
+    : mEffect(new Effect(true /*isInput*/, handle)) {}
 
 void NoiseSuppressionEffect::propertiesFromHal(const t_ns_settings& halProperties,
                                                INoiseSuppressionEffect::AllProperties* properties) {
@@ -69,10 +67,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> NoiseSuppressionEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> NoiseSuppressionEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> NoiseSuppressionEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> NoiseSuppressionEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> NoiseSuppressionEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> NoiseSuppressionEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> NoiseSuppressionEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                      setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -92,10 +112,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> NoiseSuppressionEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> NoiseSuppressionEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -117,10 +133,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> NoiseSuppressionEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> NoiseSuppressionEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/NoiseSuppressionEffect.h b/audio/effect/all-versions/default/NoiseSuppressionEffect.h
index c49bf7b..6cc45b9 100644
--- a/audio/effect/all-versions/default/NoiseSuppressionEffect.h
+++ b/audio/effect/all-versions/default/NoiseSuppressionEffect.h
@@ -55,7 +55,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -63,14 +71,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -105,7 +111,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~NoiseSuppressionEffect();
+    virtual ~NoiseSuppressionEffect() = default;
 
     void propertiesFromHal(const t_ns_settings& halProperties,
                            INoiseSuppressionEffect::AllProperties* properties);
diff --git a/audio/effect/all-versions/default/OWNERS b/audio/effect/all-versions/default/OWNERS
deleted file mode 100644
index 6fdc97c..0000000
--- a/audio/effect/all-versions/default/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-elaurent@google.com
-krocard@google.com
-mnaganov@google.com
diff --git a/audio/effect/all-versions/default/PresetReverbEffect.cpp b/audio/effect/all-versions/default/PresetReverbEffect.cpp
index 4a2a3a4..1ae8492 100644
--- a/audio/effect/all-versions/default/PresetReverbEffect.cpp
+++ b/audio/effect/all-versions/default/PresetReverbEffect.cpp
@@ -30,9 +30,8 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-PresetReverbEffect::PresetReverbEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
-
-PresetReverbEffect::~PresetReverbEffect() {}
+PresetReverbEffect::PresetReverbEffect(effect_handle_t handle)
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> PresetReverbEffect::init() {
@@ -57,10 +56,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> PresetReverbEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> PresetReverbEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> PresetReverbEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> PresetReverbEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> PresetReverbEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> PresetReverbEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> PresetReverbEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                  setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -80,10 +101,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> PresetReverbEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> PresetReverbEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -105,10 +122,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> PresetReverbEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> PresetReverbEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/PresetReverbEffect.h b/audio/effect/all-versions/default/PresetReverbEffect.h
index 58a6829..eb55e20 100644
--- a/audio/effect/all-versions/default/PresetReverbEffect.h
+++ b/audio/effect/all-versions/default/PresetReverbEffect.h
@@ -53,7 +53,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -98,7 +104,7 @@
    private:
     sp<Effect> mEffect;
 
-    virtual ~PresetReverbEffect();
+    virtual ~PresetReverbEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/TEST_MAPPING b/audio/effect/all-versions/default/TEST_MAPPING
new file mode 100644
index 0000000..b66e4e4
--- /dev/null
+++ b/audio/effect/all-versions/default/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "android.hardware.audio.effect@7.0-util_tests"
+    }
+  ]
+}
diff --git a/audio/effect/all-versions/default/VirtualizerEffect.cpp b/audio/effect/all-versions/default/VirtualizerEffect.cpp
index 1b69a90..1dce181 100644
--- a/audio/effect/all-versions/default/VirtualizerEffect.cpp
+++ b/audio/effect/all-versions/default/VirtualizerEffect.cpp
@@ -19,7 +19,9 @@
 #include "VirtualizerEffect.h"
 
 #include <memory.h>
+#include <stdlib.h>
 
+#include <HidlUtils.h>
 #include <android/log.h>
 #include <system/audio_effects/effect_virtualizer.h>
 
@@ -32,19 +34,10 @@
 namespace CPP_VERSION {
 namespace implementation {
 
-VirtualizerEffect::VirtualizerEffect(effect_handle_t handle) : mEffect(new Effect(handle)) {}
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
 
-VirtualizerEffect::~VirtualizerEffect() {}
-
-void VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
-                                             hidl_vec<SpeakerAngle>& speakerAngles) {
-    speakerAngles.resize(channelCount);
-    for (uint32_t i = 0; i < channelCount; ++i) {
-        speakerAngles[i].mask = AudioChannelBitfield(*halAngles++);
-        speakerAngles[i].azimuth = *halAngles++;
-        speakerAngles[i].elevation = *halAngles++;
-    }
-}
+VirtualizerEffect::VirtualizerEffect(effect_handle_t handle)
+    : mEffect(new Effect(false /*isInput*/, handle)) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> VirtualizerEffect::init() {
@@ -69,10 +62,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> VirtualizerEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> VirtualizerEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> VirtualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> VirtualizerEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> VirtualizerEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> VirtualizerEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> VirtualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                 setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -92,10 +107,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> VirtualizerEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> VirtualizerEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -117,10 +128,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> VirtualizerEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> VirtualizerEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
@@ -192,43 +199,117 @@
     return mEffect->getIntegerParam(VIRTUALIZER_PARAM_STRENGTH, _hidl_cb);
 }
 
-Return<void> VirtualizerEffect::getVirtualSpeakerAngles(AudioChannelBitfield mask,
-                                                        AudioDevice device,
-                                                        getVirtualSpeakerAngles_cb _hidl_cb) {
-    uint32_t channelCount =
-        audio_channel_count_from_out_mask(static_cast<audio_channel_mask_t>(mask));
+Return<void> VirtualizerEffect::getVirtualSpeakerAngles(
+#if MAJOR_VERSION <= 6
+        AudioChannelBitfield mask, AudioDevice device, getVirtualSpeakerAngles_cb _hidl_cb) {
+    audio_channel_mask_t halChannelMask = static_cast<audio_channel_mask_t>(mask);
+    audio_devices_t halDeviceType = static_cast<audio_devices_t>(device);
+#else
+        const AudioChannelMask& mask, const DeviceAddress& device,
+        getVirtualSpeakerAngles_cb _hidl_cb) {
+    audio_channel_mask_t halChannelMask;
+    if (status_t status = HidlUtils::audioChannelMaskToHal(mask, &halChannelMask);
+        status != NO_ERROR) {
+        _hidl_cb(mEffect->analyzeStatus(__func__, "audioChannelMaskToHal",
+                                        Effect::sContextConversion, status),
+                 SpeakerAngles{});
+        return Void();
+    }
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    if (status_t status = HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress);
+        status != NO_ERROR) {
+        _hidl_cb(mEffect->analyzeStatus(__func__, "deviceAddressToHal", Effect::sContextConversion,
+                                        status),
+                 SpeakerAngles{});
+        return Void();
+    }
+#endif
+    uint32_t channelCount = audio_channel_count_from_out_mask(halChannelMask);
     size_t halSpeakerAnglesSize = sizeof(int32_t) * 3 * channelCount;
-    uint32_t halParam[3] = {VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES,
-                            static_cast<audio_channel_mask_t>(mask),
-                            static_cast<audio_devices_t>(device)};
-    hidl_vec<SpeakerAngle> speakerAngles;
+    uint32_t halParam[3] = {VIRTUALIZER_PARAM_VIRTUAL_SPEAKER_ANGLES, halChannelMask,
+                            halDeviceType};
+    SpeakerAngles speakerAngles;
+    status_t status = NO_ERROR;
     Result retval = mEffect->getParameterImpl(
-        sizeof(halParam), halParam, halSpeakerAnglesSize,
-        [&](uint32_t valueSize, const void* valueData) {
-            if (valueSize > halSpeakerAnglesSize) {
-                valueSize = halSpeakerAnglesSize;
-            } else if (valueSize < halSpeakerAnglesSize) {
-                channelCount = valueSize / (sizeof(int32_t) * 3);
-            }
-            speakerAnglesFromHal(reinterpret_cast<const int32_t*>(valueData), channelCount,
-                                 speakerAngles);
-        });
+            sizeof(halParam), halParam, halSpeakerAnglesSize,
+            [&](uint32_t valueSize, const void* valueData) {
+                if (valueSize < halSpeakerAnglesSize) {
+                    channelCount = valueSize / (sizeof(int32_t) * 3);
+                }
+                status = speakerAnglesFromHal(reinterpret_cast<const int32_t*>(valueData),
+                                              channelCount, speakerAngles);
+            });
+    if (retval == Result::OK) {
+        retval = mEffect->analyzeStatus(__func__, "speakerAnglesFromHal", "", status);
+    }
     _hidl_cb(retval, speakerAngles);
     return Void();
 }
 
-Return<Result> VirtualizerEffect::forceVirtualizationMode(AudioDevice device) {
-    return mEffect->setParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE,
-                             static_cast<audio_devices_t>(device));
-}
-
 Return<void> VirtualizerEffect::getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) {
     uint32_t halMode = 0;
     Result retval = mEffect->getParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE, halMode);
+#if MAJOR_VERSION <= 6
     _hidl_cb(retval, AudioDevice(halMode));
+#else
+    DeviceAddress device;
+    (void)HidlUtils::deviceAddressFromHal(static_cast<audio_devices_t>(halMode), nullptr, &device);
+    _hidl_cb(retval, device);
+#endif
     return Void();
 }
 
+Return<Result> VirtualizerEffect::forceVirtualizationMode(
+#if MAJOR_VERSION <= 6
+        AudioDevice device) {
+    audio_devices_t halDeviceType = static_cast<audio_devices_t>(device);
+#else
+        const DeviceAddress& device) {
+    audio_devices_t halDeviceType;
+    char halDeviceAddress[AUDIO_DEVICE_MAX_ADDRESS_LEN];
+    (void)HidlUtils::deviceAddressToHal(device, &halDeviceType, halDeviceAddress);
+#endif
+    return mEffect->setParam(VIRTUALIZER_PARAM_FORCE_VIRTUALIZATION_MODE, halDeviceType);
+}
+
+#if MAJOR_VERSION <= 6
+// static
+status_t VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+                                                 hidl_vec<SpeakerAngle>& speakerAngles) {
+    speakerAngles.resize(channelCount);
+    for (uint32_t i = 0; i < channelCount; ++i) {
+        speakerAngles[i].mask = AudioChannelBitfield(*halAngles++);
+        speakerAngles[i].azimuth = *halAngles++;
+        speakerAngles[i].elevation = *halAngles++;
+    }
+    return NO_ERROR;
+}
+#else
+static int compare_channels(const void* lhs, const void* rhs) {
+    return *(int32_t*)lhs - *(int32_t*)rhs;
+}
+
+// static
+status_t VirtualizerEffect::speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+                                                 SpeakerAngles& speakerAngles) {
+    speakerAngles.azimuth.resize(channelCount);
+    speakerAngles.elevation.resize(channelCount);
+    int32_t halAnglesSorted[channelCount * 3];
+    memcpy(halAnglesSorted, halAngles, sizeof(halAnglesSorted));
+    // Ensure that channels are ordered from LSb to MSb.
+    qsort(halAnglesSorted, channelCount, sizeof(int32_t) * 3, compare_channels);
+    audio_channel_mask_t halMask = AUDIO_CHANNEL_NONE;
+    int32_t* halAnglesPtr = halAnglesSorted;
+    for (uint32_t i = 0; i < channelCount; ++i) {
+        halMask = static_cast<audio_channel_mask_t>(halMask | *halAnglesPtr++);
+        speakerAngles.azimuth[i] = *halAnglesPtr++;
+        speakerAngles.elevation[i] = *halAnglesPtr++;
+    }
+    return HidlUtils::audioChannelMaskFromHal(halMask, false /*isInput*/, &speakerAngles.mask);
+}
+#endif
+
 }  // namespace implementation
 }  // namespace CPP_VERSION
 }  // namespace effect
diff --git a/audio/effect/all-versions/default/VirtualizerEffect.h b/audio/effect/all-versions/default/VirtualizerEffect.h
index c630b2e..3ed06d1 100644
--- a/audio/effect/all-versions/default/VirtualizerEffect.h
+++ b/audio/effect/all-versions/default/VirtualizerEffect.h
@@ -39,7 +39,9 @@
 using ::android::hardware::hidl_vec;
 using ::android::hardware::Return;
 using ::android::hardware::Void;
+#if MAJOR_VERSION <= 6
 using ::android::hardware::audio::common::CPP_VERSION::implementation::AudioChannelBitfield;
+#endif
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::effect::CPP_VERSION;
 
@@ -54,7 +56,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -62,14 +72,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -96,18 +104,27 @@
     Return<bool> isStrengthSupported() override;
     Return<Result> setStrength(uint16_t strength) override;
     Return<void> getStrength(getStrength_cb _hidl_cb) override;
+    Return<void> getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) override;
+#if MAJOR_VERSION <= 6
     Return<void> getVirtualSpeakerAngles(AudioChannelBitfield mask, AudioDevice device,
                                          getVirtualSpeakerAngles_cb _hidl_cb) override;
     Return<Result> forceVirtualizationMode(AudioDevice device) override;
-    Return<void> getVirtualizationMode(getVirtualizationMode_cb _hidl_cb) override;
+#else
+    Return<void> getVirtualSpeakerAngles(const AudioChannelMask& mask, const DeviceAddress& device,
+                                         getVirtualSpeakerAngles_cb _hidl_cb) override;
+    Return<Result> forceVirtualizationMode(const DeviceAddress& device) override;
+#endif
 
-   private:
+  private:
     sp<Effect> mEffect;
 
-    virtual ~VirtualizerEffect();
+    virtual ~VirtualizerEffect() = default;
 
-    void speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
-                              hidl_vec<SpeakerAngle>& speakerAngles);
+#if MAJOR_VERSION <= 6
+    using SpeakerAngles = hidl_vec<SpeakerAngle>;
+#endif
+    static status_t speakerAnglesFromHal(const int32_t* halAngles, uint32_t channelCount,
+                                         SpeakerAngles& speakerAngles);
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/VisualizerEffect.cpp b/audio/effect/all-versions/default/VisualizerEffect.cpp
index ae533bf..80c8637 100644
--- a/audio/effect/all-versions/default/VisualizerEffect.cpp
+++ b/audio/effect/all-versions/default/VisualizerEffect.cpp
@@ -31,9 +31,9 @@
 namespace implementation {
 
 VisualizerEffect::VisualizerEffect(effect_handle_t handle)
-    : mEffect(new Effect(handle)), mCaptureSize(0), mMeasurementMode(MeasurementMode::NONE) {}
-
-VisualizerEffect::~VisualizerEffect() {}
+    : mEffect(new Effect(false /*isInput*/, handle)),
+      mCaptureSize(0),
+      mMeasurementMode(MeasurementMode::NONE) {}
 
 // Methods from ::android::hardware::audio::effect::CPP_VERSION::IEffect follow.
 Return<Result> VisualizerEffect::init() {
@@ -58,10 +58,32 @@
     return mEffect->disable();
 }
 
+#if MAJOR_VERSION <= 6
+Return<Result> VisualizerEffect::setAudioSource(AudioSource source) {
+    return mEffect->setAudioSource(source);
+}
+
 Return<Result> VisualizerEffect::setDevice(AudioDeviceBitfield device) {
     return mEffect->setDevice(device);
 }
 
+Return<Result> VisualizerEffect::setInputDevice(AudioDeviceBitfield device) {
+    return mEffect->setInputDevice(device);
+}
+#else
+Return<Result> VisualizerEffect::setAudioSource(const AudioSource& source) {
+    return mEffect->setAudioSource(source);
+}
+
+Return<Result> VisualizerEffect::setDevice(const DeviceAddress& device) {
+    return mEffect->setDevice(device);
+}
+
+Return<Result> VisualizerEffect::setInputDevice(const DeviceAddress& device) {
+    return mEffect->setInputDevice(device);
+}
+#endif
+
 Return<void> VisualizerEffect::setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                                setAndGetVolume_cb _hidl_cb) {
     return mEffect->setAndGetVolume(volumes, _hidl_cb);
@@ -81,10 +103,6 @@
     return mEffect->setConfigReverse(config, inputBufferProvider, outputBufferProvider);
 }
 
-Return<Result> VisualizerEffect::setInputDevice(AudioDeviceBitfield device) {
-    return mEffect->setInputDevice(device);
-}
-
 Return<void> VisualizerEffect::getConfig(getConfig_cb _hidl_cb) {
     return mEffect->getConfig(_hidl_cb);
 }
@@ -106,10 +124,6 @@
     return mEffect->setAuxChannelsConfig(config);
 }
 
-Return<Result> VisualizerEffect::setAudioSource(AudioSource source) {
-    return mEffect->setAudioSource(source);
-}
-
 Return<Result> VisualizerEffect::offload(const EffectOffloadParameter& param) {
     return mEffect->offload(param);
 }
diff --git a/audio/effect/all-versions/default/VisualizerEffect.h b/audio/effect/all-versions/default/VisualizerEffect.h
index 315f844..3ae4b08 100644
--- a/audio/effect/all-versions/default/VisualizerEffect.h
+++ b/audio/effect/all-versions/default/VisualizerEffect.h
@@ -53,7 +53,15 @@
     Return<Result> reset() override;
     Return<Result> enable() override;
     Return<Result> disable() override;
+#if MAJOR_VERSION <= 6
+    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> setDevice(AudioDeviceBitfield device) override;
+    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
+#else
+    Return<Result> setAudioSource(const AudioSource& source) override;
+    Return<Result> setDevice(const DeviceAddress& device) override;
+    Return<Result> setInputDevice(const DeviceAddress& device) override;
+#endif
     Return<void> setAndGetVolume(const hidl_vec<uint32_t>& volumes,
                                  setAndGetVolume_cb _hidl_cb) override;
     Return<Result> volumeChangeNotification(const hidl_vec<uint32_t>& volumes) override;
@@ -61,14 +69,12 @@
     Return<Result> setConfigReverse(
         const EffectConfig& config, const sp<IEffectBufferProviderCallback>& inputBufferProvider,
         const sp<IEffectBufferProviderCallback>& outputBufferProvider) override;
-    Return<Result> setInputDevice(AudioDeviceBitfield device) override;
     Return<void> getConfig(getConfig_cb _hidl_cb) override;
     Return<void> getConfigReverse(getConfigReverse_cb _hidl_cb) override;
     Return<void> getSupportedAuxChannelsConfigs(
         uint32_t maxConfigs, getSupportedAuxChannelsConfigs_cb _hidl_cb) override;
     Return<void> getAuxChannelsConfig(getAuxChannelsConfig_cb _hidl_cb) override;
     Return<Result> setAuxChannelsConfig(const EffectAuxChannelsConfig& config) override;
-    Return<Result> setAudioSource(AudioSource source) override;
     Return<Result> offload(const EffectOffloadParameter& param) override;
     Return<void> getDescriptor(getDescriptor_cb _hidl_cb) override;
     Return<void> prepareForProcessing(prepareForProcessing_cb _hidl_cb) override;
@@ -107,7 +113,7 @@
     uint16_t mCaptureSize;
     MeasurementMode mMeasurementMode;
 
-    virtual ~VisualizerEffect();
+    virtual ~VisualizerEffect() = default;
 };
 
 }  // namespace implementation
diff --git a/audio/effect/all-versions/default/util/Android.bp b/audio/effect/all-versions/default/util/Android.bp
new file mode 100644
index 0000000..143094d
--- /dev/null
+++ b/audio/effect/all-versions/default/util/Android.bp
@@ -0,0 +1,145 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "android.hardware.audio.effect-util_default",
+    defaults: ["hidl_defaults"],
+
+    vendor_available: true,
+
+    export_include_dirs: ["include"],
+
+    srcs: [
+        "EffectUtils.cpp",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libutils",
+        "libhidlbase",
+        "android.hardware.audio.common-util",
+    ],
+    export_shared_lib_headers: [
+        "android.hardware.audio.common-util",
+    ],
+
+    header_libs: [
+        "libaudio_system_headers",
+        "libhardware_headers",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@2.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@2.0",
+        "android.hardware.audio.common@2.0-util",
+        "android.hardware.audio.effect@2.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=2",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@4.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@4.0",
+        "android.hardware.audio.common@4.0-util",
+        "android.hardware.audio.effect@4.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=4",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@5.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.audio.common@5.0-util",
+        "android.hardware.audio.effect@5.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=5",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library_shared {
+    name: "android.hardware.audio.effect@6.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@6.0",
+        "android.hardware.audio.common@6.0-util",
+        "android.hardware.audio.effect@6.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=6",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+cc_library {
+    name: "android.hardware.audio.effect@7.0-util",
+    defaults: ["android.hardware.audio.effect-util_default"],
+    shared_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.effect@7.0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+}
+
+// Note: this isn't a VTS test, but rather a unit test
+// to verify correctness of conversion utilities.
+cc_test {
+    name: "android.hardware.audio.effect@7.0-util_tests",
+    defaults: ["android.hardware.audio.effect-util_default"],
+
+    srcs: ["tests/effectutils_tests.cpp"],
+
+    // Use static linking to allow running in presubmit on
+    // targets that don't have HAL V7.
+    static_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.common@7.0-util",
+        "android.hardware.audio.effect@7.0",
+        "android.hardware.audio.effect@7.0-util",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libxml2",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
+
+    test_suites: ["device-tests"],
+}
diff --git a/audio/effect/all-versions/default/util/EffectUtils.cpp b/audio/effect/all-versions/default/util/EffectUtils.cpp
new file mode 100644
index 0000000..1156d21
--- /dev/null
+++ b/audio/effect/all-versions/default/util/EffectUtils.cpp
@@ -0,0 +1,223 @@
+/*
+ * 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.
+ */
+
+#include <memory.h>
+
+#define LOG_TAG "EffectUtils"
+#include <log/log.h>
+
+#include <HidlUtils.h>
+#include <UuidUtils.h>
+#include <common/all-versions/VersionUtils.h>
+
+#include "util/EffectUtils.h"
+
+using ::android::hardware::audio::common::CPP_VERSION::implementation::HidlUtils;
+using ::android::hardware::audio::common::CPP_VERSION::implementation::UuidUtils;
+using ::android::hardware::audio::common::utils::EnumBitfield;
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+
+#define CONVERT_CHECKED(expr, result)                   \
+    if (status_t status = (expr); status != NO_ERROR) { \
+        result = status;                                \
+    }
+
+#if MAJOR_VERSION <= 6
+
+status_t EffectUtils::effectBufferConfigFromHal(const buffer_config_t& halConfig, bool /*isInput*/,
+                                                EffectBufferConfig* config) {
+    config->buffer.id = 0;
+    config->buffer.frameCount = 0;
+    config->samplingRateHz = halConfig.samplingRate;
+    config->channels = EnumBitfield<AudioChannelMask>(halConfig.channels);
+    config->format = AudioFormat(halConfig.format);
+    config->accessMode = EffectBufferAccess(halConfig.accessMode);
+    config->mask = EnumBitfield<EffectConfigParameters>(halConfig.mask);
+    return NO_ERROR;
+}
+
+status_t EffectUtils::effectBufferConfigToHal(const EffectBufferConfig& config,
+                                              buffer_config_t* halConfig) {
+    // Note: setting the buffers directly is considered obsolete. They need to be set
+    // using 'setProcessBuffers'.
+    halConfig->buffer.frameCount = 0;
+    halConfig->buffer.raw = nullptr;
+    halConfig->samplingRate = config.samplingRateHz;
+    halConfig->channels = static_cast<uint32_t>(config.channels);
+    // Note: The framework code does not use BP.
+    halConfig->bufferProvider.cookie = nullptr;
+    halConfig->bufferProvider.getBuffer = nullptr;
+    halConfig->bufferProvider.releaseBuffer = nullptr;
+    halConfig->format = static_cast<uint8_t>(config.format);
+    halConfig->accessMode = static_cast<uint8_t>(config.accessMode);
+    halConfig->mask = static_cast<uint8_t>(config.mask);
+    return NO_ERROR;
+}
+
+#else
+
+status_t EffectUtils::effectBufferConfigFromHal(const buffer_config_t& halConfig, bool isInput,
+                                                EffectBufferConfig* config) {
+    status_t result = NO_ERROR;
+    config->buffer.unspecified();
+    audio_config_base_t halConfigBase = {halConfig.samplingRate,
+                                         static_cast<audio_channel_mask_t>(halConfig.channels),
+                                         static_cast<audio_format_t>(halConfig.format)};
+    CONVERT_CHECKED(HidlUtils::audioConfigBaseOptionalFromHal(
+                            halConfigBase, isInput, halConfig.mask & EFFECT_CONFIG_FORMAT,
+                            halConfig.mask & EFFECT_CONFIG_SMP_RATE,
+                            halConfig.mask & EFFECT_CONFIG_CHANNELS, &config->base),
+                    result);
+    if (halConfig.mask & EFFECT_CONFIG_ACC_MODE) {
+        config->accessMode.value(EffectBufferAccess(halConfig.accessMode));
+    }
+    return result;
+}
+
+status_t EffectUtils::effectBufferConfigToHal(const EffectBufferConfig& config,
+                                              buffer_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    // Note: setting the buffers directly is considered obsolete. They need to be set
+    // using 'setProcessBuffers'.
+    halConfig->buffer.frameCount = 0;
+    halConfig->buffer.raw = nullptr;
+    audio_config_base_t halConfigBase = AUDIO_CONFIG_BASE_INITIALIZER;
+    bool formatSpecified = false, sRateSpecified = false, channelMaskSpecified = false;
+    CONVERT_CHECKED(
+            HidlUtils::audioConfigBaseOptionalToHal(config.base, &halConfigBase, &formatSpecified,
+                                                    &sRateSpecified, &channelMaskSpecified),
+            result);
+    halConfig->mask = 0;
+    if (sRateSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_SMP_RATE;
+        halConfig->samplingRate = halConfigBase.sample_rate;
+    }
+    if (channelMaskSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_CHANNELS;
+        halConfig->channels = halConfigBase.channel_mask;
+    }
+    if (formatSpecified) {
+        halConfig->mask |= EFFECT_CONFIG_FORMAT;
+        halConfig->format = halConfigBase.format;
+    }
+    // Note: The framework code does not use BP.
+    halConfig->bufferProvider.cookie = nullptr;
+    halConfig->bufferProvider.getBuffer = nullptr;
+    halConfig->bufferProvider.releaseBuffer = nullptr;
+    if (config.accessMode.getDiscriminator() ==
+        EffectBufferConfig::OptionalAccessMode::hidl_discriminator::value) {
+        halConfig->mask |= EFFECT_CONFIG_ACC_MODE;
+        halConfig->accessMode = static_cast<uint8_t>(config.accessMode.value());
+    }
+    return result;
+}
+
+#endif  // MAJOR_VERSION >= 6
+
+status_t EffectUtils::effectConfigFromHal(const effect_config_t& halConfig, bool isInput,
+                                          EffectConfig* config) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(effectBufferConfigFromHal(halConfig.inputCfg, isInput, &config->inputCfg),
+                    result);
+    CONVERT_CHECKED(effectBufferConfigFromHal(halConfig.outputCfg, isInput, &config->outputCfg),
+                    result);
+    return result;
+}
+
+status_t EffectUtils::effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig) {
+    status_t result = NO_ERROR;
+    CONVERT_CHECKED(effectBufferConfigToHal(config.inputCfg, &halConfig->inputCfg), result);
+    CONVERT_CHECKED(effectBufferConfigToHal(config.outputCfg, &halConfig->outputCfg), result);
+    return result;
+}
+
+template <std::size_t N>
+inline hidl_string charBufferFromHal(const char (&halBuf)[N]) {
+    // Even if the original field contains a non-terminated string, hidl_string
+    // adds a NUL terminator.
+    return hidl_string(halBuf, strnlen(halBuf, N));
+}
+
+template <std::size_t N>
+inline status_t charBufferToHal(const hidl_string& str, char (&halBuf)[N], const char* fieldName) {
+    static_assert(N > 0);
+    const size_t halBufChars = N - 1;  // Reserve one character for terminating NUL.
+    status_t result = NO_ERROR;
+    size_t strSize = str.size();
+    if (strSize > halBufChars) {
+        ALOGE("%s is too long: %zu (%zu max)", fieldName, strSize, halBufChars);
+        strSize = halBufChars;
+        result = BAD_VALUE;
+    }
+    strncpy(halBuf, str.c_str(), strSize);
+    halBuf[strSize] = '\0';
+    return result;
+}
+
+status_t EffectUtils::effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
+                                              EffectDescriptor* descriptor) {
+    UuidUtils::uuidFromHal(halDescriptor.type, &descriptor->type);
+    UuidUtils::uuidFromHal(halDescriptor.uuid, &descriptor->uuid);
+    descriptor->flags = EnumBitfield<EffectFlags>(halDescriptor.flags);
+    descriptor->cpuLoad = halDescriptor.cpuLoad;
+    descriptor->memoryUsage = halDescriptor.memoryUsage;
+#if MAJOR_VERSION <= 6
+    memcpy(descriptor->name.data(), halDescriptor.name, descriptor->name.size());
+    memcpy(descriptor->implementor.data(), halDescriptor.implementor,
+           descriptor->implementor.size());
+#else
+    descriptor->name = charBufferFromHal(halDescriptor.name);
+    descriptor->implementor = charBufferFromHal(halDescriptor.implementor);
+#endif
+    return NO_ERROR;
+}
+
+status_t EffectUtils::effectDescriptorToHal(const EffectDescriptor& descriptor,
+                                            effect_descriptor_t* halDescriptor) {
+    status_t result = NO_ERROR;
+    UuidUtils::uuidToHal(descriptor.type, &halDescriptor->type);
+    UuidUtils::uuidToHal(descriptor.uuid, &halDescriptor->uuid);
+    halDescriptor->flags = static_cast<uint32_t>(descriptor.flags);
+    halDescriptor->cpuLoad = descriptor.cpuLoad;
+    halDescriptor->memoryUsage = descriptor.memoryUsage;
+#if MAJOR_VERSION <= 6
+    memcpy(halDescriptor->name, descriptor.name.data(), descriptor.name.size());
+    memcpy(halDescriptor->implementor, descriptor.implementor.data(),
+           descriptor.implementor.size());
+#else
+    // According to 'dumpEffectDescriptor', 'name' and 'implementor' must be NUL-terminated.
+    CONVERT_CHECKED(charBufferToHal(descriptor.name, halDescriptor->name, "effect name"), result);
+    CONVERT_CHECKED(charBufferToHal(descriptor.implementor, halDescriptor->implementor,
+                                    "effect implementor"),
+                    result);
+#endif
+    return result;
+}
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/effect/all-versions/default/util/include/util/EffectUtils.h b/audio/effect/all-versions/default/util/include/util/EffectUtils.h
new file mode 100644
index 0000000..23963b4
--- /dev/null
+++ b/audio/effect/all-versions/default/util/include/util/EffectUtils.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+// clang-format off
+#include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
+// clang-format on
+
+#include <system/audio_effect.h>
+
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+namespace implementation {
+
+using namespace ::android::hardware::audio::effect::CPP_VERSION;
+
+struct EffectUtils {
+    static status_t effectBufferConfigFromHal(const buffer_config_t& halConfig, bool isInput,
+                                              EffectBufferConfig* config);
+    static status_t effectBufferConfigToHal(const EffectBufferConfig& config,
+                                            buffer_config_t* halConfig);
+    static status_t effectConfigFromHal(const effect_config_t& halConfig, bool isInput,
+                                        EffectConfig* config);
+    static status_t effectConfigToHal(const EffectConfig& config, effect_config_t* halConfig);
+    static status_t effectDescriptorFromHal(const effect_descriptor_t& halDescriptor,
+                                            EffectDescriptor* descriptor);
+    static status_t effectDescriptorToHal(const EffectDescriptor& descriptor,
+                                          effect_descriptor_t* halDescriptor);
+};
+
+}  // namespace implementation
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
diff --git a/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
new file mode 100644
index 0000000..d021fa0
--- /dev/null
+++ b/audio/effect/all-versions/default/util/tests/effectutils_tests.cpp
@@ -0,0 +1,173 @@
+/*
+ * 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.
+ */
+
+#include <string>
+
+#include <gtest/gtest.h>
+
+#define LOG_TAG "EffectUtils_Test"
+#include <log/log.h>
+
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#include <system/audio_effect.h>
+#include <util/EffectUtils.h>
+#include <xsdc/XsdcSupport.h>
+
+using namespace android;
+using namespace ::android::hardware::audio::common::CPP_VERSION;
+using namespace ::android::hardware::audio::effect::CPP_VERSION;
+using ::android::hardware::audio::effect::CPP_VERSION::implementation::EffectUtils;
+namespace xsd {
+using namespace ::android::audio::policy::configuration::V7_0;
+}
+
+static constexpr audio_channel_mask_t kInvalidHalChannelMask = AUDIO_CHANNEL_INVALID;
+static constexpr audio_format_t kInvalidHalFormat = AUDIO_FORMAT_INVALID;
+
+// Not generated automatically because AudioBuffer contains
+// instances of hidl_memory which can't be compared properly
+// in general case due to presence of handles.
+//
+// However, in this particular case, handles must not present
+// thus comparison is possible.
+//
+// operator== must be defined in the same namespace as the structures.
+namespace android {
+namespace hardware {
+namespace audio {
+namespace effect {
+namespace CPP_VERSION {
+inline bool operator==(const AudioBuffer& lhs, const AudioBuffer& rhs) {
+    return lhs.id == rhs.id && lhs.frameCount == rhs.frameCount && lhs.data.handle() == nullptr &&
+           rhs.data.handle() == nullptr;
+}
+
+inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
+    return lhs.buffer.getDiscriminator() == rhs.buffer.getDiscriminator() &&
+           (lhs.buffer.getDiscriminator() ==
+                    EffectBufferConfig::OptionalBuffer::hidl_discriminator::unspecified ||
+            lhs.buffer.buf() == rhs.buffer.buf()) &&
+           lhs.base == rhs.base && lhs.accessMode == rhs.accessMode;
+}
+
+inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) {
+    return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg;
+}
+}  // namespace CPP_VERSION
+}  // namespace effect
+}  // namespace audio
+}  // namespace hardware
+}  // namespace android
+
+TEST(EffectUtils, ConvertInvalidBufferConfig) {
+    buffer_config_t halInvalid;
+    EffectBufferConfig invalidChannelMask;
+    invalidChannelMask.base.channelMask.value("random string");
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigToHal(invalidChannelMask, &halInvalid));
+    EffectBufferConfig invalidFormat;
+    invalidFormat.base.format.value("random string");
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigToHal(invalidFormat, &halInvalid));
+
+    buffer_config_t halInvalidChannelMask;
+    EffectBufferConfig invalid;
+    halInvalidChannelMask.channels = kInvalidHalChannelMask;
+    halInvalidChannelMask.mask = EFFECT_CONFIG_CHANNELS;
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidChannelMask,
+                                                                false /*isInput*/, &invalid));
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidChannelMask,
+                                                                true /*isInput*/, &invalid));
+    buffer_config_t halInvalidFormat;
+    halInvalidFormat.format = (uint8_t)kInvalidHalFormat;
+    halInvalidFormat.mask = EFFECT_CONFIG_FORMAT;
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectBufferConfigFromHal(halInvalidFormat, false /*isInput*/,
+                                                                &invalid));
+    EXPECT_EQ(BAD_VALUE,
+              EffectUtils::effectBufferConfigFromHal(halInvalidFormat, true /*isInput*/, &invalid));
+}
+
+TEST(EffectUtils, ConvertBufferConfig) {
+    EffectBufferConfig empty;
+    buffer_config_t halEmpty;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(empty, &halEmpty));
+    EffectBufferConfig emptyBackOut;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halEmpty, false /*isInput*/, &emptyBackOut));
+    EXPECT_EQ(empty, emptyBackOut);
+    EffectBufferConfig emptyBackIn;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halEmpty, true /*isInput*/, &emptyBackIn));
+    EXPECT_EQ(empty, emptyBackIn);
+
+    EffectBufferConfig chanMask;
+    chanMask.base.channelMask.value(toString(xsd::AudioChannelMask::AUDIO_CHANNEL_OUT_STEREO));
+    buffer_config_t halChanMask;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(chanMask, &halChanMask));
+    EffectBufferConfig chanMaskBack;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigFromHal(halChanMask, false /*isInput*/,
+                                                               &chanMaskBack));
+    EXPECT_EQ(chanMask, chanMaskBack);
+
+    EffectBufferConfig format;
+    format.base.format.value(toString(xsd::AudioFormat::AUDIO_FORMAT_PCM_16_BIT));
+    buffer_config_t halFormat;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectBufferConfigToHal(format, &halFormat));
+    EffectBufferConfig formatBackOut;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halFormat, false /*isInput*/, &formatBackOut));
+    EXPECT_EQ(format, formatBackOut);
+    EffectBufferConfig formatBackIn;
+    EXPECT_EQ(NO_ERROR,
+              EffectUtils::effectBufferConfigFromHal(halFormat, true /*isInput*/, &formatBackIn));
+    EXPECT_EQ(format, formatBackIn);
+}
+
+TEST(EffectUtils, ConvertInvalidDescriptor) {
+    effect_descriptor_t halDesc;
+    EffectDescriptor longName{};
+    longName.name = std::string(EFFECT_STRING_LEN_MAX, 'x');
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectDescriptorToHal(longName, &halDesc));
+    EffectDescriptor longImplementor{};
+    longImplementor.implementor = std::string(EFFECT_STRING_LEN_MAX, 'x');
+    EXPECT_EQ(BAD_VALUE, EffectUtils::effectDescriptorToHal(longImplementor, &halDesc));
+}
+
+TEST(EffectUtils, ConvertDescriptor) {
+    EffectDescriptor desc{};
+    desc.name = "test";
+    desc.implementor = "foo";
+    effect_descriptor_t halDesc;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDesc));
+    EffectDescriptor descBack;
+    EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &descBack));
+    EXPECT_EQ(desc, descBack);
+}
+
+TEST(EffectUtils, ConvertNameAndImplementor) {
+    for (size_t i = 0; i < EFFECT_STRING_LEN_MAX; ++i) {
+        effect_descriptor_t halDesc{};
+        for (size_t c = 0; c < i; ++c) {  // '<' to accommodate NUL terminator.
+            halDesc.name[c] = halDesc.implementor[c] = 'A' + static_cast<char>(c);
+        }
+        EffectDescriptor desc;
+        EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorFromHal(halDesc, &desc));
+        effect_descriptor_t halDescBack;
+        EXPECT_EQ(NO_ERROR, EffectUtils::effectDescriptorToHal(desc, &halDescBack));
+        EXPECT_EQ(i, strlen(halDescBack.name));
+        EXPECT_EQ(i, strlen(halDescBack.implementor));
+        EXPECT_EQ(0, strcmp(halDesc.name, halDescBack.name));
+        EXPECT_EQ(0, strcmp(halDesc.implementor, halDescBack.implementor));
+    }
+}
diff --git a/audio/effect/all-versions/vts/OWNERS b/audio/effect/all-versions/vts/OWNERS
deleted file mode 100644
index 0ea4666..0000000
--- a/audio/effect/all-versions/vts/OWNERS
+++ /dev/null
@@ -1,5 +0,0 @@
-elaurent@google.com
-krocard@google.com
-mnaganov@google.com
-yim@google.com
-zhuoyao@google.com
diff --git a/audio/effect/all-versions/vts/functional/Android.bp b/audio/effect/all-versions/vts/functional/Android.bp
index 309aa9d..48d6474 100644
--- a/audio/effect/all-versions/vts/functional/Android.bp
+++ b/audio/effect/all-versions/vts/functional/Android.bp
@@ -14,12 +14,21 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "VtsHalAudioEffectTargetTest_default",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: [
         "VtsHalAudioEffectTargetTest.cpp",
-        "ValidateAudioEffectsConfiguration.cpp"
+        "ValidateAudioEffectsConfiguration.cpp",
     ],
     static_libs: [
         "android.hardware.audio.common.test.utility",
@@ -31,7 +40,10 @@
     header_libs: [
         "android.hardware.audio.common.util@all-versions",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
 
 cc_test {
@@ -51,7 +63,7 @@
         "-DMAJOR_VERSION=2",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_test {
@@ -71,7 +83,7 @@
         "-DMAJOR_VERSION=4",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_test {
@@ -91,7 +103,7 @@
         "-DMAJOR_VERSION=5",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
 }
 
 cc_test {
@@ -111,5 +123,26 @@
         "-DMAJOR_VERSION=6",
         "-DMINOR_VERSION=0",
         "-include common/all-versions/VersionMacro.h",
-    ]
+    ],
+}
+
+cc_test {
+    name: "VtsHalAudioEffectV7_0TargetTest",
+    defaults: ["VtsHalAudioEffectTargetTest_default"],
+    // Use test_config for vts suite.
+    // TODO(b/146104851): Add auto-gen rules and remove it.
+    test_config: "VtsHalAudioEffectV7_0TargetTest.xml",
+    static_libs: [
+        "android.hardware.audio.common@7.0",
+        "android.hardware.audio.common@7.0-enums",
+        "android.hardware.audio.effect@7.0",
+    ],
+    data: [
+        ":audio_effects_conf_V7_0",
+    ],
+    cflags: [
+        "-DMAJOR_VERSION=7",
+        "-DMINOR_VERSION=0",
+        "-include common/all-versions/VersionMacro.h",
+    ],
 }
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
index 070242f..23e7786 100644
--- a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectTargetTest.cpp
@@ -14,9 +14,13 @@
  * limitations under the License.
  */
 
+#include <vector>
+
 #define LOG_TAG "AudioEffectHidlHalTest"
 #include <android-base/logging.h>
+#if MAJOR_VERSION <= 6
 #include <system/audio.h>
+#endif
 
 #include PATH(android/hardware/audio/effect/FILE_VERSION/IEffect.h)
 #include PATH(android/hardware/audio/effect/FILE_VERSION/IEffectsFactory.h)
@@ -25,6 +29,9 @@
 #include PATH(android/hardware/audio/effect/FILE_VERSION/types.h)
 #include <android/hidl/allocator/1.0/IAllocator.h>
 #include <android/hidl/memory/1.0/IMemory.h>
+#if MAJOR_VERSION >= 7
+#include <android_audio_policy_configuration_V7_0-enums.h>
+#endif
 
 #include <common/all-versions/VersionUtils.h>
 
@@ -45,6 +52,12 @@
 using ::android::hidl::memory::V1_0::IMemory;
 using namespace ::android::hardware::audio::common::CPP_VERSION;
 using namespace ::android::hardware::audio::effect::CPP_VERSION;
+#if MAJOR_VERSION >= 7
+// Make an alias for enumerations generated from the APM config XSD.
+namespace xsd {
+using namespace ::android::audio::policy::configuration::CPP_VERSION;
+}
+#endif
 
 #ifndef ARRAY_SIZE
 #define ARRAY_SIZE(a) (sizeof(a) / sizeof(*(a)))
@@ -171,7 +184,7 @@
         effectsFactory = IEffectsFactory::getService(std::get<PARAM_FACTORY_NAME>(GetParam()));
         ASSERT_NE(nullptr, effectsFactory.get());
 
-        findAndCreateEffect(getEffectType());
+        ASSERT_NO_FATAL_FAILURE(findAndCreateEffect(getEffectType()));
         ASSERT_NE(nullptr, effect.get());
 
         Return<Result> ret = effect->init();
@@ -201,7 +214,7 @@
 
 void AudioEffectHidlTest::findAndCreateEffect(const Uuid& type) {
     Uuid effectUuid;
-    findEffectInstance(type, &effectUuid);
+    ASSERT_NO_FATAL_FAILURE(findEffectInstance(type, &effectUuid));
     Return<void> ret = effectsFactory->createEffect(
             effectUuid, 1 /*session*/, 1 /*ioHandle*/,
 #if MAJOR_VERSION >= 6
@@ -244,10 +257,18 @@
     });
     ASSERT_TRUE(ret.isOk());
     ASSERT_EQ(Result::OK, retval);
+#if MAJOR_VERSION <= 6
     ASSERT_TRUE(audio_channel_mask_is_valid(
         static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels)));
     *channelCount = audio_channel_count_from_out_mask(
         static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels));
+#else
+    ASSERT_EQ(AudioConfigBaseOptional::ChannelMask::hidl_discriminator::value,
+              currentConfig.outputCfg.base.channelMask.getDiscriminator());
+    *channelCount = android::audio::policy::configuration::V7_0::getChannelCount(
+            currentConfig.outputCfg.base.channelMask.value());
+    ASSERT_NE(*channelCount, 0);
+#endif
 }
 
 TEST_P(AudioEffectHidlTest, Close) {
@@ -291,6 +312,47 @@
     EXPECT_EQ(Result::OK, ret2);
 }
 
+#if MAJOR_VERSION >= 7
+std::vector<EffectBufferConfig> generateInvalidConfigs(const EffectBufferConfig& src) {
+    std::vector<EffectBufferConfig> result;
+    EffectBufferConfig invalidFormat = src;
+    invalidFormat.base.format.value("random_string");
+    result.push_back(std::move(invalidFormat));
+    EffectBufferConfig invalidChannelMask = src;
+    invalidChannelMask.base.channelMask.value("random_string");
+    result.push_back(std::move(invalidChannelMask));
+    return result;
+}
+
+TEST_P(AudioEffectHidlTest, SetConfigInvalidArguments) {
+    description("Verify that invalid arguments are rejected by SetConfig");
+    Result retval = Result::NOT_INITIALIZED;
+    EffectConfig currentConfig;
+    Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
+        retval = r;
+        if (r == Result::OK) {
+            currentConfig = conf;
+        }
+    });
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::OK, retval);
+    for (const auto& invalidInputCfg : generateInvalidConfigs(currentConfig.inputCfg)) {
+        EffectConfig invalidConfig = currentConfig;
+        invalidConfig.inputCfg = invalidInputCfg;
+        Return<Result> ret = effect->setConfig(invalidConfig, nullptr, nullptr);
+        EXPECT_TRUE(ret.isOk());
+        EXPECT_EQ(Result::INVALID_ARGUMENTS, ret);
+    }
+    for (const auto& invalidOutputCfg : generateInvalidConfigs(currentConfig.outputCfg)) {
+        EffectConfig invalidConfig = currentConfig;
+        invalidConfig.outputCfg = invalidOutputCfg;
+        Return<Result> ret = effect->setConfig(invalidConfig, nullptr, nullptr);
+        EXPECT_TRUE(ret.isOk());
+        EXPECT_EQ(Result::INVALID_ARGUMENTS, ret);
+    }
+}
+#endif
+
 TEST_P(AudioEffectHidlTest, GetConfigReverse) {
     description("Verify that GetConfigReverse does not crash");
     Return<void> ret = effect->getConfigReverse([&](Result, const EffectConfig&) {});
@@ -334,11 +396,22 @@
            rhs.data.handle() == nullptr;
 }
 
+#if MAJOR_VERSION <= 6
 inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
-    return lhs.buffer == rhs.buffer && lhs.samplingRateHz == rhs.samplingRateHz &&
-           lhs.channels == rhs.channels && lhs.format == rhs.format &&
+    return lhs.buffer == rhs.buffer &&
+           lhs.samplingRateHz == rhs.samplingRateHz && lhs.channels == rhs.channels &&
+           lhs.format == rhs.format &&
            lhs.accessMode == rhs.accessMode && lhs.mask == rhs.mask;
 }
+#else
+inline bool operator==(const EffectBufferConfig& lhs, const EffectBufferConfig& rhs) {
+    return lhs.buffer.getDiscriminator() == rhs.buffer.getDiscriminator() &&
+           (lhs.buffer.getDiscriminator() ==
+                    EffectBufferConfig::OptionalBuffer::hidl_discriminator::unspecified ||
+            lhs.buffer.buf() == rhs.buffer.buf()) &&
+           lhs.base == rhs.base && lhs.accessMode == rhs.accessMode;
+}
+#endif  // MAJOR_VERSION <= 6
 
 inline bool operator==(const EffectConfig& lhs, const EffectConfig& rhs) {
     return lhs.inputCfg == rhs.inputCfg && lhs.outputCfg == rhs.outputCfg;
@@ -389,9 +462,24 @@
     EXPECT_EQ(Result::OK, ret);
 }
 
+#if MAJOR_VERSION >= 7
+TEST_P(AudioEffectHidlTest, SetDeviceInvalidDeviceAddress) {
+    description("Verify that invalid device address is rejected by SetDevice");
+    DeviceAddress device{.deviceType = "random_string"};
+    Return<Result> ret = effect->setDevice(device);
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(Result::INVALID_ARGUMENTS, ret);
+}
+#endif
+
 TEST_P(AudioEffectHidlTest, SetDevice) {
     description("Verify that SetDevice works for an output chain effect");
+#if MAJOR_VERSION <= 6
     Return<Result> ret = effect->setDevice(mkEnumBitfield(AudioDevice::OUT_SPEAKER));
+#else
+    DeviceAddress device{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_OUT_SPEAKER)};
+    Return<Result> ret = effect->setDevice(device);
+#endif
     EXPECT_TRUE(ret.isOk());
     EXPECT_EQ(Result::OK, ret);
 }
@@ -439,24 +527,51 @@
     EXPECT_TRUE(ret.isOk());
 }
 
+#if MAJOR_VERSION >= 7
+TEST_P(AudioEffectHidlTest, SetInputDeviceInvalidDeviceAddress) {
+    description("Verify that invalid device address is rejected by SetInputDevice");
+    DeviceAddress device{.deviceType = "random_string"};
+    Return<Result> ret = effect->setInputDevice(device);
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_TRUE(ret == Result::INVALID_ARGUMENTS || ret == Result::NOT_SUPPORTED)
+            << ::testing::PrintToString(ret);
+}
+#endif
+
 TEST_P(AudioEffectHidlTest, SetInputDevice) {
     description("Verify that SetInputDevice does not crash");
+#if MAJOR_VERSION <= 6
     Return<Result> ret = effect->setInputDevice(mkEnumBitfield(AudioDevice::IN_BUILTIN_MIC));
+#else
+    DeviceAddress device{.deviceType = toString(xsd::AudioDevice::AUDIO_DEVICE_IN_BUILTIN_MIC)};
+    Return<Result> ret = effect->setInputDevice(device);
+#endif
     EXPECT_TRUE(ret.isOk());
 }
 
+#if MAJOR_VERSION >= 7
+TEST_P(AudioEffectHidlTest, SetInvalidAudioSource) {
+    description("Verify that an invalid audio source is rejected by SetAudioSource");
+    Return<Result> ret = effect->setAudioSource("random_string");
+    ASSERT_TRUE(ret.isOk());
+    EXPECT_TRUE(ret == Result::INVALID_ARGUMENTS || ret == Result::NOT_SUPPORTED)
+            << ::testing::PrintToString(ret);
+}
+#endif
+
 TEST_P(AudioEffectHidlTest, SetAudioSource) {
     description("Verify that SetAudioSource does not crash");
+#if MAJOR_VERSION <= 6
     Return<Result> ret = effect->setAudioSource(AudioSource::MIC);
+#else
+    Return<Result> ret = effect->setAudioSource(toString(xsd::AudioSource::AUDIO_SOURCE_MIC));
+#endif
     EXPECT_TRUE(ret.isOk());
 }
 
 TEST_P(AudioEffectHidlTest, Offload) {
     description("Verify that calling Offload method does not crash");
-    EffectOffloadParameter offloadParam;
-    offloadParam.isOffload = false;
-    offloadParam.ioHandle = static_cast<int>(AudioHandleConsts::AUDIO_IO_HANDLE_NONE);
-    Return<Result> ret = effect->offload(offloadParam);
+    Return<Result> ret = effect->offload(EffectOffloadParameter{});
     EXPECT_TRUE(ret.isOk());
 }
 
@@ -845,3 +960,9 @@
                                    IEffectsFactory::descriptor)),
                            ::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE)),
         EffectParameterToString);
+// When the VTS test runs on a device lacking the corresponding HAL version the parameter
+// list is empty, this isn't a problem.
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectsFactoryHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEffectHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EqualizerAudioEffectHidlTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LoudnessEnhancerAudioEffectHidlTest);
diff --git a/audio/effect/all-versions/vts/functional/VtsHalAudioEffectV7_0TargetTest.xml b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectV7_0TargetTest.xml
new file mode 100644
index 0000000..e609756
--- /dev/null
+++ b/audio/effect/all-versions/vts/functional/VtsHalAudioEffectV7_0TargetTest.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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 VtsHalAudioEffectV7_0TargetTest.">
+    <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 class="com.android.tradefed.targetprep.StopServicesSetup"/>
+
+    <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+        <option name="run-command" value="setprop vts.native_server.on 1"/>
+        <option name="teardown-command" value="setprop vts.native_server.on 0"/>
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalAudioEffectV7_0TargetTest->/data/local/tmp/VtsHalAudioEffectV7_0TargetTest" />
+        <option name="push" value="audio_effects_conf_V7_0.xsd->/data/local/tmp/audio_effects_conf_V7_0.xsd" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalAudioEffectV7_0TargetTest" />
+    </test>
+</configuration>
diff --git a/audio/policy/1.0/vts/functional/Android.bp b/audio/policy/1.0/vts/functional/Android.bp
index a5ddee5..cccb2fc 100644
--- a/audio/policy/1.0/vts/functional/Android.bp
+++ b/audio/policy/1.0/vts/functional/Android.bp
@@ -1,6 +1,14 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalAudioPolicyV1_0TargetTest",
-    defaults: ["vts_target_tests_defaults"],
     srcs: [
         "ValidateEngineConfiguration.cpp",
     ],
diff --git a/audio/policy/1.0/xml/Android.bp b/audio/policy/1.0/xml/Android.bp
index 6da7b5a..403278c 100644
--- a/audio/policy/1.0/xml/Android.bp
+++ b/audio/policy/1.0/xml/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_policy_engine_conf_V1_0",
     srcs: ["audio_policy_engine_configuration.xsd"],
diff --git a/audio/policy/1.0/xml/pfw_schemas/Android.bp b/audio/policy/1.0/xml/pfw_schemas/Android.bp
index 8054dc5..5d669c2 100644
--- a/audio/policy/1.0/xml/pfw_schemas/Android.bp
+++ b/audio/policy/1.0/xml/pfw_schemas/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 xsd_config {
     name: "audio_policy_engine_configurable_configuration_V1_0",
     srcs: ["AllSchemas.xsd"],
diff --git a/authsecret/1.0/Android.bp b/authsecret/1.0/Android.bp
index 3b84c3b..b14284b 100644
--- a/authsecret/1.0/Android.bp
+++ b/authsecret/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.authsecret@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IAuthSecret.hal",
     ],
diff --git a/authsecret/1.0/default/Android.bp b/authsecret/1.0/default/Android.bp
index b6ea3c4..5bf389b 100644
--- a/authsecret/1.0/default/Android.bp
+++ b/authsecret/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.authsecret@1.0-service",
     init_rc: ["android.hardware.authsecret@1.0-service.rc"],
diff --git a/authsecret/1.0/vts/functional/Android.bp b/authsecret/1.0/vts/functional/Android.bp
index c49d374..853b4dd 100644
--- a/authsecret/1.0/vts/functional/Android.bp
+++ b/authsecret/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalAuthSecretV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
index f27f266..c09b265 100644
--- a/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
+++ b/authsecret/1.0/vts/functional/VtsHalAuthSecretV1_0TargetTest.cpp
@@ -81,6 +81,7 @@
     authsecret->primaryUserCredential(WRONG_SECRET);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthSecretHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, AuthSecretHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IAuthSecret::descriptor)),
diff --git a/authsecret/aidl/Android.bp b/authsecret/aidl/Android.bp
new file mode 100644
index 0000000..8fe9f27
--- /dev/null
+++ b/authsecret/aidl/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.authsecret",
+    vendor_available: true,
+    srcs: ["android/hardware/authsecret/*.aidl"],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/authsecret/aidl/aidl_api/android.hardware.authsecret/current/android/hardware/authsecret/IAuthSecret.aidl b/authsecret/aidl/aidl_api/android.hardware.authsecret/current/android/hardware/authsecret/IAuthSecret.aidl
new file mode 100644
index 0000000..14e8325
--- /dev/null
+++ b/authsecret/aidl/aidl_api/android.hardware.authsecret/current/android/hardware/authsecret/IAuthSecret.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.authsecret;
+@VintfStability
+interface IAuthSecret {
+  oneway void setPrimaryUserCredential(in byte[] secret);
+}
diff --git a/authsecret/aidl/android/hardware/authsecret/IAuthSecret.aidl b/authsecret/aidl/android/hardware/authsecret/IAuthSecret.aidl
new file mode 100644
index 0000000..3849ec0
--- /dev/null
+++ b/authsecret/aidl/android/hardware/authsecret/IAuthSecret.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.authsecret;
+
+/**
+ * This security HAL allows vendor components to be cryptographically tied to
+ * the primary user's credential. For example, security hardware can require
+ * proof that the credential is known before applying updates.
+ *
+ */
+@VintfStability
+interface IAuthSecret {
+    /**
+     * When the primary user is unlocked, this method is passed a secret to
+     * prove that is has been successfully unlocked. The primary user can either
+     * be unlocked by a person entering their credential or by another party
+     * using an escrow token e.g. a device administrator.
+     *
+     * The first time this is called, the secret must be used to provision state
+     * that depends on the primary user's secret. The same secret must be passed
+     * on each call until the next factory reset.
+     *
+     * Upon factory reset, any dependence on the secret must be removed as that
+     * secret is now lost and must never be derived again. A new secret must be
+     * created for the new primary user which must be used to newly provision
+     * state the first time this method is called after factory reset.
+     *
+     * The secret must be at least 16 bytes, or the secret must be dropped.
+     *
+     * @param secret blob derived from the primary user's credential.
+     */
+    oneway void setPrimaryUserCredential(in byte[] secret);
+}
diff --git a/authsecret/aidl/default/Android.bp b/authsecret/aidl/default/Android.bp
new file mode 100644
index 0000000..a6c0bc4
--- /dev/null
+++ b/authsecret/aidl/default/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.authsecret-service.example",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.authsecret-service.example.rc"],
+    vintf_fragments: ["android.hardware.authsecret-service.example.xml"],
+    vendor: true,
+    srcs: [
+        "service.cpp",
+        "AuthSecret.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.authsecret-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+    ],
+}
diff --git a/authsecret/aidl/default/AuthSecret.cpp b/authsecret/aidl/default/AuthSecret.cpp
new file mode 100644
index 0000000..9645e4d
--- /dev/null
+++ b/authsecret/aidl/default/AuthSecret.cpp
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2018 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 "AuthSecret.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace authsecret {
+
+// Methods from ::android::hardware::authsecret::IAuthSecret follow.
+::ndk::ScopedAStatus AuthSecret::setPrimaryUserCredential(const std::vector<uint8_t>& in_secret) {
+    (void)in_secret;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace authsecret
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/authsecret/aidl/default/AuthSecret.h b/authsecret/aidl/default/AuthSecret.h
new file mode 100644
index 0000000..b40fc48
--- /dev/null
+++ b/authsecret/aidl/default/AuthSecret.h
@@ -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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/authsecret/BnAuthSecret.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace authsecret {
+
+struct AuthSecret : public BnAuthSecret {
+    AuthSecret() = default;
+
+    // Methods from ::android::hardware::authsecret::IAuthSecret follow.
+    ::ndk::ScopedAStatus setPrimaryUserCredential(const std::vector<uint8_t>& in_secret) override;
+
+};
+
+} // namespace authsecret
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/authsecret/aidl/default/android.hardware.authsecret-service.example.rc b/authsecret/aidl/default/android.hardware.authsecret-service.example.rc
new file mode 100644
index 0000000..fef6e24
--- /dev/null
+++ b/authsecret/aidl/default/android.hardware.authsecret-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.authsecret_default /vendor/bin/hw/android.hardware.authsecret-service.example
+    class hal
+    user hsm
+    group hsm
diff --git a/authsecret/aidl/default/android.hardware.authsecret-service.example.xml b/authsecret/aidl/default/android.hardware.authsecret-service.example.xml
new file mode 100644
index 0000000..9d4e162
--- /dev/null
+++ b/authsecret/aidl/default/android.hardware.authsecret-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.authsecret</name>
+        <version>1</version>
+        <interface>
+            <name>IAuthSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/authsecret/aidl/default/service.cpp b/authsecret/aidl/default/service.cpp
new file mode 100644
index 0000000..efecf10
--- /dev/null
+++ b/authsecret/aidl/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "AuthSecret.h"
+
+using ::aidl::android::hardware::authsecret::AuthSecret;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<AuthSecret> authsecret = ndk::SharedRefBase::make<AuthSecret>();
+
+    const std::string instance = std::string() + AuthSecret::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(authsecret->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return -1; // Should never be reached
+}
diff --git a/authsecret/aidl/vts/Android.bp b/authsecret/aidl/vts/Android.bp
new file mode 100644
index 0000000..dca7046
--- /dev/null
+++ b/authsecret/aidl/vts/Android.bp
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2018 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalAuthSecretTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalAuthSecretTargetTest.cpp"],
+    static_libs: ["android.hardware.authsecret-V1-ndk_platform"],
+    shared_libs: ["libbinder_ndk"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    require_root: true,
+}
diff --git a/authsecret/aidl/vts/OWNERS b/authsecret/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/authsecret/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/authsecret/aidl/vts/VtsHalAuthSecretTargetTest.cpp b/authsecret/aidl/vts/VtsHalAuthSecretTargetTest.cpp
new file mode 100644
index 0000000..31c2834
--- /dev/null
+++ b/authsecret/aidl/vts/VtsHalAuthSecretTargetTest.cpp
@@ -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.
+ */
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/authsecret/IAuthSecret.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::authsecret::IAuthSecret;
+
+using ::ndk::SpAIBinder;
+
+/**
+ * There is no expected behaviour that can be tested so these tests check the
+ * HAL doesn't crash with different execution orders.
+ */
+class AuthSecretAidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        authsecret = IAuthSecret::fromBinder(
+            SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(authsecret, nullptr);
+
+        // Notify LSS to generate PIN code '1234' and corresponding secret.
+        (void)system("cmd lock_settings set-pin 1234");
+
+        // All tests must enroll the correct secret first as this cannot be changed
+        // without a factory reset and the order of tests could change.
+        authsecret->setPrimaryUserCredential(CORRECT_SECRET);
+    }
+
+    static void TearDownTestSuite() {
+        // clean up PIN code after testing
+        (void)system("cmd lock_settings clear --old 1234");
+    }
+
+    std::shared_ptr<IAuthSecret> authsecret;
+    std::vector<uint8_t> CORRECT_SECRET{61, 93, 124, 240, 5, 0, 7, 201, 9, 129, 11, 12, 0, 14, 0, 16};
+    std::vector<uint8_t> WRONG_SECRET{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+};
+
+/* Provision the primary user with a secret. */
+TEST_P(AuthSecretAidlTest, provisionPrimaryUserCredential) {
+    // Secret provisioned by SetUp()
+}
+
+/* Provision the primary user with a secret and pass the secret again. */
+TEST_P(AuthSecretAidlTest, provisionPrimaryUserCredentialAndPassAgain) {
+    // Secret provisioned by SetUp()
+    authsecret->setPrimaryUserCredential(CORRECT_SECRET);
+}
+
+/* Provision the primary user with a secret and pass the secret again repeatedly. */
+TEST_P(AuthSecretAidlTest, provisionPrimaryUserCredentialAndPassAgainMultipleTimes) {
+    // Secret provisioned by SetUp()
+    constexpr int N = 5;
+    for (int i = 0; i < N; ++i) {
+        authsecret->setPrimaryUserCredential(CORRECT_SECRET);
+    }
+}
+
+/* Provision the primary user with a secret and then pass the wrong secret. This
+ * should never happen and is an framework bug if it does. As the secret is
+ * wrong, the HAL implementation may not be able to function correctly but it
+ * should fail gracefully. */
+TEST_P(AuthSecretAidlTest, provisionPrimaryUserCredentialAndWrongSecret) {
+    // Secret provisioned by SetUp()
+    authsecret->setPrimaryUserCredential(WRONG_SECRET);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthSecretAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, AuthSecretAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IAuthSecret::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/automotive/OWNERS b/automotive/OWNERS
index 83ee63c..fb3e3d6 100644
--- a/automotive/OWNERS
+++ b/automotive/OWNERS
@@ -3,3 +3,4 @@
 pfg@google.com
 gurunagarajan@google.com
 keunyoung@google.com
+felipeal@google.com
diff --git a/automotive/audiocontrol/1.0/Android.bp b/automotive/audiocontrol/1.0/Android.bp
index 7ef7909..628793b 100644
--- a/automotive/audiocontrol/1.0/Android.bp
+++ b/automotive/audiocontrol/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.audiocontrol@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAudioControl.hal",
diff --git a/automotive/audiocontrol/1.0/default/Android.bp b/automotive/audiocontrol/1.0/default/Android.bp
index ae4b805..7180a70 100644
--- a/automotive/audiocontrol/1.0/default/Android.bp
+++ b/automotive/audiocontrol/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.audiocontrol@1.0-service",
     defaults: ["hidl_defaults"],
@@ -19,7 +28,7 @@
     relative_install_path: "hw",
     srcs: [
         "AudioControl.cpp",
-        "service.cpp"
+        "service.cpp",
     ],
     init_rc: ["android.hardware.automotive.audiocontrol@1.0-service.rc"],
 
@@ -31,3 +40,16 @@
     ],
     vintf_fragments: ["audiocontrol_manifest.xml"],
 }
+
+filegroup {
+    name: "audiocontrolV1.0_source",
+    srcs: [
+        "AudioControl.cpp",
+    ],
+}
+
+cc_library_headers {
+    name: "audiocontrolV1.0_header",
+    host_supported: true,
+    export_include_dirs: ["."],
+}
diff --git a/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
new file mode 100644
index 0000000..78f5b52
--- /dev/null
+++ b/automotive/audiocontrol/1.0/default/test/fuzzer/Android.bp
@@ -0,0 +1,52 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_fuzz {
+    name: "audiocontrolV1.0_fuzzer",
+    host_supported: true,
+    srcs: [
+        "audiocontrolV1.0_fuzzer.cpp",
+        ":audiocontrolV1.0_source",
+    ],
+    header_libs: [
+        "audiocontrolV1.0_header",
+    ],
+    shared_libs: [
+        "android.hardware.automotive.audiocontrol@1.0",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 533764,
+    },
+}
diff --git a/automotive/audiocontrol/1.0/default/test/fuzzer/audiocontrolV1.0_fuzzer.cpp b/automotive/audiocontrol/1.0/default/test/fuzzer/audiocontrolV1.0_fuzzer.cpp
new file mode 100644
index 0000000..268df08
--- /dev/null
+++ b/automotive/audiocontrol/1.0/default/test/fuzzer/audiocontrolV1.0_fuzzer.cpp
@@ -0,0 +1,41 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+#include <AudioControl.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+using ::android::sp;
+using ::android::hardware::automotive::audiocontrol::V1_0::ContextNumber;
+using ::android::hardware::automotive::audiocontrol::V1_0::implementation::AudioControl;
+
+namespace android::hardware::automotive::audiocontrol::V1_0::implementation::fuzzer {
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    if (size < 1) {
+        return 0;
+    }
+    if (sp<AudioControl> audioControl = new AudioControl(); audioControl != nullptr) {
+        FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+        ContextNumber contextNumber = static_cast<ContextNumber>(fdp.ConsumeIntegral<uint32_t>());
+        audioControl->getBusForContext(contextNumber);
+        audioControl->setBalanceTowardRight(fdp.ConsumeFloatingPoint<float>());
+        audioControl->setFadeTowardFront(fdp.ConsumeFloatingPoint<float>());
+    }
+    return 0;
+}
+}  // namespace android::hardware::automotive::audiocontrol::V1_0::implementation::fuzzer
diff --git a/automotive/audiocontrol/1.0/vts/functional/Android.bp b/automotive/audiocontrol/1.0/vts/functional/Android.bp
index 1bb8e88..15c480a 100644
--- a/automotive/audiocontrol/1.0/vts/functional/Android.bp
+++ b/automotive/audiocontrol/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalAudioControlV1_0TargetTest",
     srcs: [
diff --git a/automotive/audiocontrol/2.0/Android.bp b/automotive/audiocontrol/2.0/Android.bp
index 2a9f849..4d1fdbc 100644
--- a/automotive/audiocontrol/2.0/Android.bp
+++ b/automotive/audiocontrol/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.audiocontrol@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAudioControl.hal",
@@ -13,8 +19,9 @@
         "IFocusListener.hal",
     ],
     interfaces: [
-        "android.hidl.base@1.0",
         "android.hardware.audio.common@6.0",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
     ],
     gen_java: true,
 }
diff --git a/automotive/audiocontrol/2.0/default/Android.bp b/automotive/audiocontrol/2.0/default/Android.bp
index 44ad028..1b8cef7 100644
--- a/automotive/audiocontrol/2.0/default/Android.bp
+++ b/automotive/audiocontrol/2.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.audiocontrol@2.0-service",
     defaults: ["hidl_defaults"],
diff --git a/automotive/audiocontrol/2.0/vts/functional/Android.bp b/automotive/audiocontrol/2.0/vts/functional/Android.bp
index ac20509..cb7a54d 100644
--- a/automotive/audiocontrol/2.0/vts/functional/Android.bp
+++ b/automotive/audiocontrol/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalAudioControlV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/automotive/can/1.0/Android.bp b/automotive/can/1.0/Android.bp
index 2221e66..47db9f8 100644
--- a/automotive/can/1.0/Android.bp
+++ b/automotive/can/1.0/Android.bp
@@ -1,11 +1,18 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-NCSA
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.can@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICanBus.hal",
diff --git a/automotive/can/1.0/default/Android.bp b/automotive/can/1.0/default/Android.bp
index f5cf425..879decc 100644
--- a/automotive/can/1.0/default/Android.bp
+++ b/automotive/can/1.0/default/Android.bp
@@ -14,6 +14,16 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    //   SPDX-license-identifier-NCSA
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.automotive.can@defaults",
     cpp_std: "experimental",
diff --git a/automotive/can/1.0/default/libc++fs/Android.bp b/automotive/can/1.0/default/libc++fs/Android.bp
index 7ab1c28..0641991 100644
--- a/automotive/can/1.0/default/libc++fs/Android.bp
+++ b/automotive/can/1.0/default/libc++fs/Android.bp
@@ -17,6 +17,15 @@
 // TODO(152067309): Stop building this yourself once it's ABI stable and has
 // been made vendor available. Just use libc++fs instead of this.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-NCSA
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.automotive@libc++fsdefaults",
     local_include_dirs: ["include"],
diff --git a/automotive/can/1.0/default/libnetdevice/Android.bp b/automotive/can/1.0/default/libnetdevice/Android.bp
index 31e5ad0..757d8e6 100644
--- a/automotive/can/1.0/default/libnetdevice/Android.bp
+++ b/automotive/can/1.0/default/libnetdevice/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.can@libnetdevice",
     defaults: ["android.hardware.automotive.can@defaults"],
diff --git a/automotive/can/1.0/hidl-utils/Android.bp b/automotive/can/1.0/hidl-utils/Android.bp
index 63b07c9..7453e10 100644
--- a/automotive/can/1.0/hidl-utils/Android.bp
+++ b/automotive/can/1.0/hidl-utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.automotive.can@hidl-utils-lib",
     export_include_dirs: ["include"],
diff --git a/automotive/can/1.0/tools/Android.bp b/automotive/can/1.0/tools/Android.bp
index a6c40d9..a0807aa 100644
--- a/automotive/can/1.0/tools/Android.bp
+++ b/automotive/can/1.0/tools/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "canhalctrl",
     defaults: ["android.hardware.automotive.can@defaults"],
diff --git a/automotive/can/1.0/tools/configurator/Android.bp b/automotive/can/1.0/tools/configurator/Android.bp
index 2c4bc1d..cc826bc 100644
--- a/automotive/can/1.0/tools/configurator/Android.bp
+++ b/automotive/can/1.0/tools/configurator/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "canhalconfigurator",
     init_rc: ["canhalconfigurator.rc"],
diff --git a/automotive/can/1.0/tools/configurator/proto/Android.bp b/automotive/can/1.0/tools/configurator/proto/Android.bp
index 05e1205..7b10791 100644
--- a/automotive/can/1.0/tools/configurator/proto/Android.bp
+++ b/automotive/can/1.0/tools/configurator/proto/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.can@1.x-config-format",
     defaults: ["android.hardware.automotive.can@defaults"],
diff --git a/automotive/can/1.0/tools/libcanhaltools/Android.bp b/automotive/can/1.0/tools/libcanhaltools/Android.bp
index cee9eef..a25748b 100644
--- a/automotive/can/1.0/tools/libcanhaltools/Android.bp
+++ b/automotive/can/1.0/tools/libcanhaltools/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.can@libcanhaltools",
     defaults: ["android.hardware.automotive.can@defaults"],
diff --git a/automotive/can/1.0/vts/functional/Android.bp b/automotive/can/1.0/vts/functional/Android.bp
index d020750..1d51492 100644
--- a/automotive/can/1.0/vts/functional/Android.bp
+++ b/automotive/can/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.automotive.can@vts-defaults",
     defaults: [
diff --git a/automotive/can/1.0/vts/utils/Android.bp b/automotive/can/1.0/vts/utils/Android.bp
index d03ead3..81d7ed1 100644
--- a/automotive/can/1.0/vts/utils/Android.bp
+++ b/automotive/can/1.0/vts/utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.can@vts-utils-lib",
     defaults: ["android.hardware.automotive.can@defaults"],
diff --git a/automotive/evs/1.0/Android.bp b/automotive/evs/1.0/Android.bp
index 51f8e20..04b9c78 100644
--- a/automotive/evs/1.0/Android.bp
+++ b/automotive/evs/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.evs@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IEvsCamera.hal",
diff --git a/automotive/evs/1.0/default/Android.bp b/automotive/evs/1.0/default/Android.bp
index 6e28ab1..c4c624f 100644
--- a/automotive/evs/1.0/default/Android.bp
+++ b/automotive/evs/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.evs@1.0-service",
     defaults: ["hidl_defaults"],
diff --git a/automotive/evs/1.0/vts/functional/Android.bp b/automotive/evs/1.0/vts/functional/Android.bp
index 74d5122..f1b57d7 100644
--- a/automotive/evs/1.0/vts/functional/Android.bp
+++ b/automotive/evs/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalEvsV1_0TargetTest",
     srcs: [
diff --git a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
index 54862a2..7fe7a33 100644
--- a/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
+++ b/automotive/evs/1.0/vts/functional/VtsHalEvsV1_0TargetTest.cpp
@@ -579,7 +579,7 @@
     }
 }
 
-
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EvsHidlTest);
 INSTANTIATE_TEST_SUITE_P(
     PerInstance,
     EvsHidlTest,
diff --git a/automotive/evs/1.1/Android.bp b/automotive/evs/1.1/Android.bp
index f9bccef..a72f971 100644
--- a/automotive/evs/1.1/Android.bp
+++ b/automotive/evs/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.evs@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IEvsCamera.hal",
diff --git a/automotive/evs/1.1/default/Android.bp b/automotive/evs/1.1/default/Android.bp
index 6e5695d..ea1851f 100644
--- a/automotive/evs/1.1/default/Android.bp
+++ b/automotive/evs/1.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.evs@1.1-service",
     defaults: ["hidl_defaults"],
diff --git a/automotive/evs/1.1/vts/functional/Android.bp b/automotive/evs/1.1/vts/functional/Android.bp
index d61f0a8..cbc2150 100644
--- a/automotive/evs/1.1/vts/functional/Android.bp
+++ b/automotive/evs/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalEvsV1_1TargetTest",
     srcs: [
diff --git a/automotive/evs/1.1/vts/fuzzing/Android.bp b/automotive/evs/1.1/vts/fuzzing/Android.bp
index 48427ee..1764821 100644
--- a/automotive/evs/1.1/vts/fuzzing/Android.bp
+++ b/automotive/evs/1.1/vts/fuzzing/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.automotive.evs@fuzz-defaults",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/automotive/evs/common/utils/default/Android.bp b/automotive/evs/common/utils/default/Android.bp
index 776ef81..18b6ee6 100644
--- a/automotive/evs/common/utils/default/Android.bp
+++ b/automotive/evs/common/utils/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     host_supported: true,
     name: "android.hardware.automotive.evs@common-default-lib",
diff --git a/automotive/evs/common/utils/default/test/fuzz/Android.bp b/automotive/evs/common/utils/default/test/fuzz/Android.bp
index 105ec68..a2cf273 100644
--- a/automotive/evs/common/utils/default/test/fuzz/Android.bp
+++ b/automotive/evs/common/utils/default/test/fuzz/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_fuzz {
     host_supported: true,
     name : "FormatConvertFuzzer_copyNV21toRGB32",
diff --git a/automotive/occupant_awareness/aidl/Android.bp b/automotive/occupant_awareness/aidl/Android.bp
index 4127f33..26c53fa 100644
--- a/automotive/occupant_awareness/aidl/Android.bp
+++ b/automotive/occupant_awareness/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.automotive.occupant_awareness",
     vendor_available: true,
diff --git a/automotive/occupant_awareness/aidl/default/Android.bp b/automotive/occupant_awareness/aidl/default/Android.bp
index 1b2fba2..4db43bb 100644
--- a/automotive/occupant_awareness/aidl/default/Android.bp
+++ b/automotive/occupant_awareness/aidl/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.occupant_awareness@1.0-service",
     init_rc: ["android.hardware.automotive.occupant_awareness@1.0-service.rc"],
@@ -27,6 +36,6 @@
         "libbase",
         "libbinder_ndk",
         "libutils",
-        "android.hardware.automotive.occupant_awareness-ndk_platform",
+        "android.hardware.automotive.occupant_awareness-V1-ndk_platform",
     ],
 }
diff --git a/automotive/occupant_awareness/aidl/mock/Android.bp b/automotive/occupant_awareness/aidl/mock/Android.bp
index 4b30866..275eb22 100644
--- a/automotive/occupant_awareness/aidl/mock/Android.bp
+++ b/automotive/occupant_awareness/aidl/mock/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.occupant_awareness@1.0-service_mock",
     relative_install_path: "hw",
@@ -27,6 +36,6 @@
         "libbase",
         "libbinder_ndk",
         "libutils",
-        "android.hardware.automotive.occupant_awareness-ndk_platform",
+        "android.hardware.automotive.occupant_awareness-V1-ndk_platform",
     ],
 }
diff --git a/automotive/occupant_awareness/aidl/vts/functional/Android.bp b/automotive/occupant_awareness/aidl/vts/functional/Android.bp
index 514b0af..f248aa9 100644
--- a/automotive/occupant_awareness/aidl/vts/functional/Android.bp
+++ b/automotive/occupant_awareness/aidl/vts/functional/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalOccupantAwarenessV1_0TargetTest",
     defaults: [
@@ -9,7 +18,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.automotive.occupant_awareness-cpp",
+        "android.hardware.automotive.occupant_awareness-V1-cpp",
     ],
     test_suites: [
         "vts",
diff --git a/automotive/sv/1.0/Android.bp b/automotive/sv/1.0/Android.bp
index 769bdc6..a6b1ba7 100644
--- a/automotive/sv/1.0/Android.bp
+++ b/automotive/sv/1.0/Android.bp
@@ -1,24 +1,30 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.sv@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
-        "ISurroundViewStream.hal",
-        "ISurroundViewSession.hal",
         "ISurroundView2dSession.hal",
         "ISurroundView3dSession.hal",
         "ISurroundViewService.hal",
+        "ISurroundViewSession.hal",
+        "ISurroundViewStream.hal",
     ],
     interfaces: [
-        "android.hidl.base@1.0",
         "android.hardware.graphics.common@1.0",
         "android.hardware.graphics.common@1.1",
         "android.hardware.graphics.common@1.2",
+        "android.hidl.base@1.0",
     ],
     gen_java: true,
 }
diff --git a/automotive/sv/1.0/default/Android.bp b/automotive/sv/1.0/default/Android.bp
index 8417949..da974a0 100644
--- a/automotive/sv/1.0/default/Android.bp
+++ b/automotive/sv/1.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.automotive.sv@1.0-service",
     vendor: true,
diff --git a/automotive/sv/1.0/vts/functional/Android.bp b/automotive/sv/1.0/vts/functional/Android.bp
index d5d72a6..1ff3450 100644
--- a/automotive/sv/1.0/vts/functional/Android.bp
+++ b/automotive/sv/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSurroundViewV1_0TargetTest",
     srcs: [
diff --git a/automotive/vehicle/2.0/Android.bp b/automotive/vehicle/2.0/Android.bp
index 0e73d85..e2164b1 100644
--- a/automotive/vehicle/2.0/Android.bp
+++ b/automotive/vehicle/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.automotive.vehicle@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IVehicle.hal",
diff --git a/automotive/vehicle/2.0/default/Android.bp b/automotive/vehicle/2.0/default/Android.bp
index 590adc5..c13efde 100644
--- a/automotive/vehicle/2.0/default/Android.bp
+++ b/automotive/vehicle/2.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "vhal_v2_0_defaults",
     shared_libs: [
@@ -32,7 +41,7 @@
     defaults: ["vhal_v2_0_defaults"],
     shared_libs: [
         "libbinder_ndk",
-        "carwatchdog_aidl_interface-ndk_platform",
+        "carwatchdog_aidl_interface-V2-ndk_platform",
     ],
 }
 
@@ -82,6 +91,7 @@
         "impl/vhal_v2_0/LinearFakeValueGenerator.cpp",
         "impl/vhal_v2_0/JsonFakeValueGenerator.cpp",
         "impl/vhal_v2_0/GeneratorHub.cpp",
+        "impl/vhal_v2_0/qemu_pipe.cpp",
     ],
     local_include_dirs: ["common/include/vhal_v2_0"],
     export_include_dirs: ["impl"],
@@ -95,7 +105,6 @@
         "libprotobuf-cpp-lite",
     ],
     static_libs: [
-        "libqemu_pipe",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
     ],
 }
@@ -210,6 +219,31 @@
         "android.hardware.automotive.vehicle@2.0-manager-lib",
         "android.hardware.automotive.vehicle@2.0-default-impl-lib",
         "android.hardware.automotive.vehicle@2.0-libproto-native",
-        "libqemu_pipe",
     ],
 }
+
+cc_fuzz {
+    name: "vehicleManager_fuzzer",
+    vendor: true,
+    defaults: ["vhal_v2_0_target_defaults"],
+    whole_static_libs: ["android.hardware.automotive.vehicle@2.0-manager-lib"],
+    srcs: [
+        "tests/fuzzer/VehicleManager_fuzzer.cpp",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcutils",
+        "libbinder_ndk",
+    ],
+    header_libs: ["libbase_headers"],
+    local_include_dirs: [
+        "common/include",
+        "tests",
+    ],
+    fuzz_config: {
+        cc: [
+            "android-media-fuzzing-reports@google.com",
+        ],
+        componentid: 533764,
+    },
+}
diff --git a/automotive/vehicle/2.0/default/VehicleService.cpp b/automotive/vehicle/2.0/default/VehicleService.cpp
index cf1e4ba..ef29560 100644
--- a/automotive/vehicle/2.0/default/VehicleService.cpp
+++ b/automotive/vehicle/2.0/default/VehicleService.cpp
@@ -22,6 +22,7 @@
 
 #include <android/binder_process.h>
 #include <utils/Looper.h>
+#include <vhal_v2_0/EmulatedUserHal.h>
 #include <vhal_v2_0/EmulatedVehicleConnector.h>
 #include <vhal_v2_0/EmulatedVehicleHal.h>
 #include <vhal_v2_0/VehicleHalManager.h>
@@ -34,7 +35,8 @@
 int main(int /* argc */, char* /* argv */ []) {
     auto store = std::make_unique<VehiclePropertyStore>();
     auto connector = std::make_unique<impl::EmulatedVehicleConnector>();
-    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get());
+    auto userHal = connector->getEmulatedUserHal();
+    auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get(), userHal);
     auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get());
     auto service = std::make_unique<VehicleHalManager>(hal.get());
     connector->setValuePool(hal->getValuePool());
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
index 1d51600..3ef42f1 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp
@@ -15,8 +15,13 @@
  */
 #define LOG_TAG "DefaultVehicleHal_v2_0"
 
-#include <android/log.h>
 #include <android-base/macros.h>
+#include <android-base/properties.h>
+#include <android/log.h>
+#include <dirent.h>
+#include <sys/system_properties.h>
+#include <fstream>
+#include <regex>
 
 #include "EmulatedVehicleHal.h"
 #include "JsonFakeValueGenerator.h"
@@ -102,6 +107,30 @@
     mVehicleClient->registerPropertyValueCallback(std::bind(&EmulatedVehicleHal::onPropertyValue,
                                                             this, std::placeholders::_1,
                                                             std::placeholders::_2));
+
+    mInitVhalValueOverride =
+            android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false);
+    if (mInitVhalValueOverride) {
+        getAllPropertiesOverride();
+    }
+}
+
+void EmulatedVehicleHal::getAllPropertiesOverride() {
+    if (auto dir = opendir("/vendor/etc/vhaloverride/")) {
+        std::regex reg_json(".*[.]json", std::regex::icase);
+        while (auto f = readdir(dir)) {
+            if (!regex_match(f->d_name, reg_json)) {
+                continue;
+            }
+            std::string file = "/vendor/etc/vhaloverride/" + std::string(f->d_name);
+            JsonFakeValueGenerator tmpGenerator(file);
+
+            std::vector<VehiclePropValue> propvalues = tmpGenerator.getAllEvents();
+            mVehiclePropertiesOverride.insert(std::end(mVehiclePropertiesOverride),
+                                              std::begin(propvalues), std::end(propvalues));
+        }
+        closedir(dir);
+    }
 }
 
 VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
@@ -210,6 +239,14 @@
         return StatusCode::NOT_AVAILABLE;
     }
 
+    if (mInEmulator && propValue.prop == toInt(VehicleProperty::DISPLAY_BRIGHTNESS)) {
+        // Emulator does not support remote brightness control, b/139959479
+        // do not send it down so that it does not bring unnecessary property change event
+        // return other error code, such NOT_AVAILABLE, causes Emulator to be freezing
+        // TODO: return StatusCode::NOT_AVAILABLE once the above issue is fixed
+        return StatusCode::OK;
+    }
+
     /**
      * After checking all conditions, such as the property is available, a real vhal will
      * sent the events to Car ECU to take actions.
@@ -279,12 +316,21 @@
                 }
             } else {
                 prop.value = it.initialValue;
+                if (mInitVhalValueOverride) {
+                    for (auto& itOverride : mVehiclePropertiesOverride) {
+                        if (itOverride.prop == cfg.prop) {
+                            prop.value = itOverride.value;
+                        }
+                    }
+                }
             }
             mPropStore->writeValue(prop, shouldUpdateStatus);
         }
     }
     initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
     initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
+    mInEmulator = isInEmulator();
+    ALOGD("mInEmulator=%s", mInEmulator ? "true" : "false");
 }
 
 std::vector<VehiclePropConfig> EmulatedVehicleHal::listProperties()  {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
index 30f6bfa..eb38d7d 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.h
@@ -63,6 +63,7 @@
     //  Methods from EmulatedVehicleHalIface
     bool setPropertyFromVehicle(const VehiclePropValue& propValue) override;
     std::vector<VehiclePropValue> getAllProperties() const override;
+    void getAllPropertiesOverride();
 
 private:
     constexpr std::chrono::nanoseconds hertzToNanoseconds(float hz) const {
@@ -87,6 +88,9 @@
     std::unordered_set<int32_t> mHvacPowerProps;
     RecurrentTimer mRecurrentTimer;
     VehicleHalClient* mVehicleClient;
+    bool mInEmulator;
+    bool mInitVhalValueOverride;
+    std::vector<VehiclePropValue> mVehiclePropertiesOverride;
     EmulatedUserHal* mEmulatedUserHal;
 };
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
index 8677f83..b62918f 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.cpp
@@ -48,6 +48,22 @@
     mNumOfIterations = v.int32Values.size() < 2 ? -1 : v.int32Values[1];
 }
 
+JsonFakeValueGenerator::JsonFakeValueGenerator(std::string path) {
+    std::ifstream ifs(path);
+    if (!ifs) {
+        ALOGE("%s: couldn't open %s for parsing.", __func__, path.c_str());
+    }
+    mGenCfg = {
+        .index = 0,
+        .events = parseFakeValueJson(ifs),
+    };
+    mNumOfIterations = mGenCfg.events.size();
+}
+
+std::vector<VehiclePropValue> JsonFakeValueGenerator::getAllEvents() {
+    return mGenCfg.events;
+}
+
 VehiclePropValue JsonFakeValueGenerator::nextEvent() {
     VehiclePropValue generatedValue;
     if (!hasNext()) {
@@ -80,11 +96,12 @@
 std::vector<VehiclePropValue> JsonFakeValueGenerator::parseFakeValueJson(std::istream& is) {
     std::vector<VehiclePropValue> fakeVhalEvents;
 
-    Json::Reader reader;
+    Json::CharReaderBuilder builder;
     Json::Value rawEvents;
-    if (!reader.parse(is, rawEvents)) {
+    std::string errorMessage;
+    if (!Json::parseFromStream(builder, is, &rawEvents, &errorMessage)) {
         ALOGE("%s: Failed to parse fake data JSON file. Error: %s", __func__,
-              reader.getFormattedErrorMessages().c_str());
+              errorMessage.c_str());
         return fakeVhalEvents;
     }
 
@@ -109,6 +126,7 @@
 
         Json::Value rawEventValue = rawEvent["value"];
         auto& value = event.value;
+        int32_t count;
         switch (getPropType(event.prop)) {
             case VehiclePropertyType::BOOLEAN:
             case VehiclePropertyType::INT32:
@@ -126,6 +144,13 @@
             case VehiclePropertyType::STRING:
                 value.stringValue = rawEventValue.asString();
                 break;
+            case VehiclePropertyType::INT32_VEC:
+                value.int32Values.resize(rawEventValue.size());
+                count = 0;
+                for (auto& it : rawEventValue) {
+                    value.int32Values[count++] = it.asInt();
+                }
+                break;
             case VehiclePropertyType::MIXED:
                 copyMixedValueJson(value, rawEventValue);
                 if (isDiagnosticProperty(event.prop)) {
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
index 70575f7..dc8ff66 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/JsonFakeValueGenerator.h
@@ -41,9 +41,12 @@
 
 public:
     JsonFakeValueGenerator(const VehiclePropValue& request);
+    JsonFakeValueGenerator(std::string path);
+
     ~JsonFakeValueGenerator() = default;
 
     VehiclePropValue nextEvent();
+    std::vector<VehiclePropValue> getAllEvents();
 
     bool hasNext();
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
index f024287..81e7c78 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/PipeComm.cpp
@@ -18,9 +18,9 @@
 
 #include <android/hardware/automotive/vehicle/2.0/IVehicle.h>
 #include <log/log.h>
-#include <qemu_pipe.h>
 
 #include "PipeComm.h"
+#include "qemu_pipe.h"
 
 #define CAR_SERVICE_NAME "pipe:qemud:car"
 
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
index 263ca62..f7d0854 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.cpp
@@ -44,7 +44,7 @@
     mSocketComm = std::make_unique<SocketComm>(this);
     mSocketComm->start();
 
-    if (android::base::GetBoolProperty("ro.kernel.qemu", false)) {
+    if (isInEmulator()) {
         ALOGI("Starting PipeComm");
         mPipeComm = std::make_unique<PipeComm>(this);
         mPipeComm->start();
@@ -226,6 +226,10 @@
     return proto_msg_converter::toProto(protoVal, *val);
 }
 
+bool isInEmulator() {
+    return android::base::GetBoolProperty("ro.boot.qemu", false);
+}
+
 }  // impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
index 82947be..434d79b 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/VehicleEmulator.h
@@ -95,6 +95,9 @@
     std::unique_ptr<PipeComm> mPipeComm;
 };
 
+// determine if it's running inside Android Emulator
+bool isInEmulator();
+
 }  // impl
 
 }  // namespace V2_0
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
index c5b9ed6..6e85ae9 100644
--- a/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/proto/Android.bp
@@ -13,6 +13,15 @@
 // limitations under the License.
 
 // Vehicle HAL Protobuf library
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-libproto-native",
     vendor: true,
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp
new file mode 100644
index 0000000..cf1a002
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.cpp
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2011 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 "qemu_pipe.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
+
+using android::base::ReadFully;
+using android::base::WriteFully;
+
+// Define QEMU_PIPE_DEBUG if you want to print error messages when an error
+// occurs during pipe operations. The macro should simply take a printf-style
+// formatting string followed by optional arguments.
+#ifndef QEMU_PIPE_DEBUG
+#define QEMU_PIPE_DEBUG(...) (void)0
+#endif
+
+int qemu_pipe_open(const char* pipeName) {
+    if (!pipeName) {
+        errno = EINVAL;
+        return -1;
+    }
+
+    int fd = TEMP_FAILURE_RETRY(open("/dev/qemu_pipe", O_RDWR));
+    if (fd < 0) {
+        QEMU_PIPE_DEBUG("%s: Could not open /dev/qemu_pipe: %s", __FUNCTION__, strerror(errno));
+        return -1;
+    }
+
+    // Write the pipe name, *including* the trailing zero which is necessary.
+    size_t pipeNameLen = strlen(pipeName);
+    if (WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+
+    // now, add 'pipe:' prefix and try again
+    // Note: host side will wait for the trailing '\0' to start
+    // service lookup.
+    const char pipe_prefix[] = "pipe:";
+    if (WriteFully(fd, pipe_prefix, strlen(pipe_prefix)) &&
+        WriteFully(fd, pipeName, pipeNameLen + 1U)) {
+        return fd;
+    }
+    QEMU_PIPE_DEBUG("%s: Could not write to %s pipe service: %s", __FUNCTION__, pipeName,
+                    strerror(errno));
+    close(fd);
+    return -1;
+}
+
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len) {
+    char header[5];
+    snprintf(header, sizeof(header), "%04zx", len);
+    if (!WriteFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    if (!WriteFully(fd, buff, len)) {
+        QEMU_PIPE_DEBUG("Can't write qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return 0;
+}
+
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len) {
+    char header[5];
+    if (!ReadFully(fd, header, 4)) {
+        QEMU_PIPE_DEBUG("Can't read qemud frame header: %s", strerror(errno));
+        return -1;
+    }
+    header[4] = '\0';
+    size_t size;
+    if (sscanf(header, "%04zx", &size) != 1) {
+        QEMU_PIPE_DEBUG("Malformed qemud frame header: [%.*s]", 4, header);
+        return -1;
+    }
+    if (size > len) {
+        QEMU_PIPE_DEBUG("Oversized qemud frame (% bytes, expected <= %)", size, len);
+        return -1;
+    }
+    if (!ReadFully(fd, buff, size)) {
+        QEMU_PIPE_DEBUG("Could not read qemud frame payload: %s", strerror(errno));
+        return -1;
+    }
+    return size;
+}
diff --git a/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h
new file mode 100644
index 0000000..0987498
--- /dev/null
+++ b/automotive/vehicle/2.0/default/impl/vhal_v2_0/qemu_pipe.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2011 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_CORE_INCLUDE_QEMU_PIPE_H
+#define ANDROID_CORE_INCLUDE_QEMU_PIPE_H
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Try to open a new Qemu fast-pipe. This function returns a file descriptor
+// that can be used to communicate with a named service managed by the
+// emulator.
+//
+// This file descriptor can be used as a standard pipe/socket descriptor.
+//
+// 'pipeName' is the name of the emulator service you want to connect to,
+// and should begin with 'pipe:' (e.g. 'pipe:camera' or 'pipe:opengles').
+// For backward compatibility, the 'pipe:' prefix can be omitted, and in
+// that case, qemu_pipe_open will add it for you.
+
+// On success, return a valid file descriptor, or -1/errno on failure. E.g.:
+//
+// EINVAL  -> unknown/unsupported pipeName
+// ENOSYS  -> fast pipes not available in this system.
+//
+// ENOSYS should never happen, except if you're trying to run within a
+// misconfigured emulator.
+//
+// You should be able to open several pipes to the same pipe service,
+// except for a few special cases (e.g. GSM modem), where EBUSY will be
+// returned if more than one client tries to connect to it.
+int qemu_pipe_open(const char* pipeName);
+
+// Send a framed message |buff| of |len| bytes through the |fd| descriptor.
+// This really adds a 4-hexchar prefix describing the payload size.
+// Returns 0 on success, and -1 on error.
+int qemu_pipe_frame_send(int fd, const void* buff, size_t len);
+
+// Read a frame message from |fd|, and store it into |buff| of |len| bytes.
+// If the framed message is larger than |len|, then this returns -1 and the
+// content is lost. Otherwise, this returns the size of the message. NOTE:
+// empty messages are possible in a framed wire protocol and do not mean
+// end-of-stream.
+int qemu_pipe_frame_recv(int fd, void* buff, size_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* ANDROID_CORE_INCLUDE_QEMU_PIPE_H */
diff --git a/automotive/vehicle/2.0/default/tests/fuzzer/README.md b/automotive/vehicle/2.0/default/tests/fuzzer/README.md
new file mode 100644
index 0000000..3fa0d3f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/fuzzer/README.md
@@ -0,0 +1,66 @@
+# Fuzzer for android.hardware.automotive.vehicle@2.0-manager-lib
+
+## Plugin Design Considerations
+The fuzzer plugin for android.hardware.automotive.vehicle@2.0-manager-lib is
+designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+Vehicle Manager supports the following parameters:
+1. Vehicle Property (parameter name: `vehicleProp`)
+2. Diagnostic Integer Sensor Index (parameter name: `diagnosticIntIndex`)
+3. Diagnostic Float Sensor Index (parameter name: `diagnosticFloatIndex`)
+4. Availability Message Type (parameter name: `availabilityMsgType`)
+5. Subscription Message Type (parameter name: `subscriptionMsgType`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `vehicleProp` | 0.`VehicleProperty::INVALID` 1.`VehicleProperty::HVAC_FAN_SPEED` 2.`VehicleProperty::INFO_MAKE` 3.`VehicleProperty::DISPLAY_BRIGHTNESS`  4.`VehicleProperty::INFO_FUEL_CAPACITY` 5.`VehicleProperty::HVAC_SEAT_TEMPERATURE`| Value obtained from FuzzedDataProvider |
+| `diagnosticIntIndex`   | 0.`DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS` 1.`DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON` 2.`DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT` 3.`DiagnosticIntegerSensorIndex::FUEL_TYPE`  | Value obtained from FuzzedDataProvider |
+| `diagnosticFloatIndex`   | 0.`DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD` 1.`DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1` 2.`DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1` 3.`DiagnosticFloatSensorIndex::THROTTLE_POSITION`  | Value obtained from FuzzedDataProvider |
+| `availabilityMsgType`   | 0.`VmsMessageType::AVAILABILITY_CHANGE` 1.`VmsMessageType::AVAILABILITY_RESPONSE` | Value obtained from FuzzedDataProvider |
+| `subscriptionMsgType`   | 0.`VmsMessageType::SUBSCRIPTIONS_CHANGE` 1.`VmsMessageType::SUBSCRIPTIONS_RESPONSE` | Value obtained from FuzzedDataProvider |
+
+This also ensures that the plugin is always deterministic for any given input.
+
+## Build
+
+This describes steps to build vehicleManager_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+  $ mm -j$(nproc) vehicleManager_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR
+```
+  $ adb shell mkdir /data/local/tmp/CORPUS_DIR
+```
+
+##### Some Additional steps needed to run the vehicleManager_fuzzer successfully on device
+
+1. Push the following libraries from /vendor/lib/ and /vendor/lib64/ folders of your workspace to the device's /vendor/lib/ and /vendor/lib64/ :
+```
+1.1  android.hardware.automotive.vehicle@2.0.so
+1.2  carwatchdog_aidl_interface-V2-ndk_platform.so
+```
+2. Now, reboot the device using command
+```
+  $ adb reboot
+```
+
+##### To run the fuzzer on device
+```
+  $ adb sync data
+  $ adb shell LD_LIBRARY_PATH=/vendor/lib64 /data/fuzz/${TARGET_ARCH}/vehicleManager_fuzzer/vendor/vehicleManager_fuzzer /data/local/tmp/CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp
new file mode 100644
index 0000000..796c08f
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.cpp
@@ -0,0 +1,433 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include "VehicleManager_fuzzer.h"
+#include <utils/SystemClock.h>
+#include <vhal_v2_0/Obd2SensorStore.h>
+#include <vhal_v2_0/WatchdogClient.h>
+
+namespace android::hardware::automotive::vehicle::V2_0::fuzzer {
+
+using ::aidl::android::automotive::watchdog::TimeoutLength;
+using ::android::elapsedRealtimeNano;
+using ::android::Looper;
+using ::android::sp;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::automotive::vehicle::V2_0::DiagnosticFloatSensorIndex;
+using ::android::hardware::automotive::vehicle::V2_0::DiagnosticIntegerSensorIndex;
+using ::android::hardware::automotive::vehicle::V2_0::kCustomComplexProperty;
+using ::android::hardware::automotive::vehicle::V2_0::kVehicleProperties;
+using ::android::hardware::automotive::vehicle::V2_0::MockedVehicleCallback;
+using ::android::hardware::automotive::vehicle::V2_0::Obd2SensorStore;
+using ::android::hardware::automotive::vehicle::V2_0::recyclable_ptr;
+using ::android::hardware::automotive::vehicle::V2_0::StatusCode;
+using ::android::hardware::automotive::vehicle::V2_0::SubscribeFlags;
+using ::android::hardware::automotive::vehicle::V2_0::SubscribeOptions;
+using ::android::hardware::automotive::vehicle::V2_0::VehicleAreaConfig;
+using ::android::hardware::automotive::vehicle::V2_0::VehicleHal;
+using ::android::hardware::automotive::vehicle::V2_0::VehicleHalManager;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropConfig;
+using ::android::hardware::automotive::vehicle::V2_0::VehicleProperty;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropertyAccess;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropertyChangeMode;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropertyStore;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropertyType;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropValue;
+using ::android::hardware::automotive::vehicle::V2_0::VehiclePropValuePool;
+using ::android::hardware::automotive::vehicle::V2_0::VmsMessageType;
+using ::android::hardware::automotive::vehicle::V2_0::WatchdogClient;
+using ::android::hardware::automotive::vehicle::V2_0::vms::createAvailabilityRequest;
+using ::android::hardware::automotive::vehicle::V2_0::vms::createBaseVmsMessage;
+using ::android::hardware::automotive::vehicle::V2_0::vms::createPublisherIdRequest;
+using ::android::hardware::automotive::vehicle::V2_0::vms::createStartSessionMessage;
+using ::android::hardware::automotive::vehicle::V2_0::vms::createSubscriptionsRequest;
+using ::android::hardware::automotive::vehicle::V2_0::vms::getAvailableLayers;
+using ::android::hardware::automotive::vehicle::V2_0::vms::getSequenceNumberForAvailabilityState;
+using ::android::hardware::automotive::vehicle::V2_0::vms::getSequenceNumberForSubscriptionsState;
+using ::android::hardware::automotive::vehicle::V2_0::vms::hasServiceNewlyStarted;
+using ::android::hardware::automotive::vehicle::V2_0::vms::isAvailabilitySequenceNumberNewer;
+using ::android::hardware::automotive::vehicle::V2_0::vms::isSequenceNumberNewer;
+using ::android::hardware::automotive::vehicle::V2_0::vms::isValidVmsMessage;
+using ::android::hardware::automotive::vehicle::V2_0::vms::parseData;
+using ::android::hardware::automotive::vehicle::V2_0::vms::parseMessageType;
+using ::android::hardware::automotive::vehicle::V2_0::vms::parsePublisherIdResponse;
+using ::android::hardware::automotive::vehicle::V2_0::vms::parseStartSessionMessage;
+using ::android::hardware::automotive::vehicle::V2_0::vms::VmsLayer;
+using ::android::hardware::automotive::vehicle::V2_0::vms::VmsLayerAndPublisher;
+using ::android::hardware::automotive::vehicle::V2_0::vms::VmsLayerOffering;
+using ::android::hardware::automotive::vehicle::V2_0::vms::VmsOffers;
+
+constexpr const char kCarMake[] = "Default Car";
+constexpr VehicleProperty kVehicleProp[] = {VehicleProperty::INVALID,
+                                            VehicleProperty::HVAC_FAN_SPEED,
+                                            VehicleProperty::INFO_MAKE,
+                                            VehicleProperty::DISPLAY_BRIGHTNESS,
+                                            VehicleProperty::INFO_FUEL_CAPACITY,
+                                            VehicleProperty::HVAC_SEAT_TEMPERATURE};
+constexpr DiagnosticIntegerSensorIndex kDiagnosticIntIndex[] = {
+        DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
+        DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON,
+        DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT,
+        DiagnosticIntegerSensorIndex::FUEL_TYPE};
+constexpr DiagnosticFloatSensorIndex kDiagnosticFloatIndex[] = {
+        DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD,
+        DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1,
+        DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1,
+        DiagnosticFloatSensorIndex::THROTTLE_POSITION};
+constexpr size_t kVehiclePropArrayLength = std::size(kVehicleProp);
+constexpr size_t kIntSensorArrayLength = std::size(kDiagnosticIntIndex);
+constexpr size_t kFloatSensorArrayLength = std::size(kDiagnosticFloatIndex);
+constexpr VmsMessageType kAvailabilityMessageType[] = {VmsMessageType::AVAILABILITY_CHANGE,
+                                                       VmsMessageType::AVAILABILITY_RESPONSE};
+constexpr VmsMessageType kSubscriptionMessageType[] = {VmsMessageType::SUBSCRIPTIONS_CHANGE,
+                                                       VmsMessageType::SUBSCRIPTIONS_RESPONSE};
+
+MockedVehicleHal::VehiclePropValuePtr MockedVehicleHal::get(
+        const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
+    VehiclePropValuePtr pValue = nullptr;
+    if (outStatus == nullptr) {
+        return pValue;
+    }
+    auto property = static_cast<VehicleProperty>(requestedPropValue.prop);
+    int32_t areaId = requestedPropValue.areaId;
+    *outStatus = StatusCode::OK;
+
+    switch (property) {
+        case VehicleProperty::INFO_MAKE:
+            pValue = getValuePool()->obtainString(kCarMake);
+            break;
+        case VehicleProperty::INFO_FUEL_CAPACITY:
+            if (mFuelCapacityAttemptsLeft-- > 0) {
+                *outStatus = StatusCode::TRY_AGAIN;
+            } else {
+                pValue = getValuePool()->obtainFloat(42.42);
+            }
+            break;
+        default:
+            if (requestedPropValue.prop == kCustomComplexProperty) {
+                pValue = getValuePool()->obtainComplex();
+                pValue->value.int32Values = hidl_vec<int32_t>{10, 20};
+                pValue->value.int64Values = hidl_vec<int64_t>{30, 40};
+                pValue->value.floatValues = hidl_vec<float_t>{1.1, 2.2};
+                pValue->value.bytes = hidl_vec<uint8_t>{1, 2, 3};
+                pValue->value.stringValue = kCarMake;
+                break;
+            }
+            auto key = makeKey(toInt(property), areaId);
+            pValue = getValuePool()->obtain(mValues[key]);
+    }
+
+    if (*outStatus == StatusCode::OK && pValue.get() != nullptr) {
+        pValue->prop = toInt(property);
+        pValue->areaId = areaId;
+        pValue->timestamp = elapsedRealtimeNano();
+    }
+
+    return pValue;
+}
+
+void VehicleHalManagerFuzzer::process(const uint8_t* data, size_t size) {
+    mFuzzedDataProvider = new FuzzedDataProvider(data, size);
+    invokeDebug();
+    invokePropConfigs();
+    invokeSubscribe();
+    invokeSetAndGetValues();
+    invokeObd2SensorStore();
+    invokeVmsUtils();
+    invokeVehiclePropStore();
+    invokeWatchDogClient();
+}
+
+void VehicleHalManagerFuzzer::invokeDebug() {
+    hidl_string debugOption = mFuzzedDataProvider->PickValueInArray(
+            {"--help", "--list", "--get", "--set", "", "invalid"});
+    hidl_handle fd = {};
+    fd.setTo(native_handle_create(/*numFds=*/1, /*numInts=*/0), /*shouldOwn=*/true);
+
+    mManager->debug(fd, {});
+    mManager->debug(fd, {debugOption});
+}
+
+void VehicleHalManagerFuzzer::invokePropConfigs() {
+    int32_t vehicleProp1 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    int32_t vehicleProp2 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+
+    hidl_vec<int32_t> properties = {vehicleProp1, vehicleProp2};
+
+    mManager->getPropConfigs(properties,
+                             []([[maybe_unused]] StatusCode status,
+                                [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
+
+    mManager->getPropConfigs({toInt(kVehicleProp[abs(vehicleProp1) % kVehiclePropArrayLength])},
+                             []([[maybe_unused]] StatusCode status,
+                                [[maybe_unused]] const hidl_vec<VehiclePropConfig>& c) {});
+
+    mManager->getAllPropConfigs(
+            []([[maybe_unused]] const hidl_vec<VehiclePropConfig>& propConfigs) {});
+}
+
+void VehicleHalManagerFuzzer::invokeSubscribe() {
+    int32_t vehicleProp1 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    int32_t vehicleProp2 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    int32_t vehicleProp3 = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+
+    const auto prop1 = toInt(kVehicleProp[abs(vehicleProp1) % kVehiclePropArrayLength]);
+    sp<MockedVehicleCallback> cb = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options = {
+            SubscribeOptions{.propId = prop1, .flags = SubscribeFlags::EVENTS_FROM_CAR}};
+
+    mManager->subscribe(cb, options);
+
+    auto unsubscribedValue = mObjectPool->obtain(VehiclePropertyType::INT32);
+    unsubscribedValue->prop = toInt(kVehicleProp[abs(vehicleProp2) % kVehiclePropArrayLength]);
+
+    mHal->sendPropEvent(std::move(unsubscribedValue));
+    cb->getReceivedEvents();
+    cb->waitForExpectedEvents(0);
+
+    auto subscribedValue = mObjectPool->obtain(VehiclePropertyType::INT32);
+    subscribedValue->prop = toInt(kVehicleProp[abs(vehicleProp2) % kVehiclePropArrayLength]);
+    subscribedValue->value.int32Values[0] = INT32_MAX;
+
+    cb->reset();
+    VehiclePropValue actualValue(*subscribedValue.get());
+    mHal->sendPropEvent(std::move(subscribedValue));
+    cb->waitForExpectedEvents(1);
+    mManager->unsubscribe(cb, prop1);
+
+    sp<MockedVehicleCallback> cb2 = new MockedVehicleCallback();
+
+    hidl_vec<SubscribeOptions> options2 = {
+            SubscribeOptions{
+                    .propId = toInt(kVehicleProp[abs(vehicleProp3) % kVehiclePropArrayLength]),
+                    .flags = SubscribeFlags::EVENTS_FROM_CAR},
+    };
+
+    mManager->subscribe(cb2, options2);
+
+    mHal->sendHalError(StatusCode::TRY_AGAIN,
+                       toInt(kVehicleProp[abs(vehicleProp3) % kVehiclePropArrayLength]),
+                       /*areaId=*/0);
+}
+
+void VehicleHalManagerFuzzer::invokeSetAndGetValues() {
+    uint32_t vehicleProp1 =
+            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
+    uint32_t vehicleProp2 =
+            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
+    uint32_t vehicleProp3 =
+            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kVehiclePropArrayLength - 1);
+
+    invokeGet(kCustomComplexProperty, 0);
+    invokeGet(toInt(kVehicleProp[vehicleProp2]), 0);
+    invokeGet(toInt(kVehicleProp[vehicleProp1]), 0);
+
+    auto expectedValue = mObjectPool->obtainInt32(mFuzzedDataProvider->ConsumeIntegral<int32_t>());
+    mObjectPool->obtainInt64(mFuzzedDataProvider->ConsumeIntegral<int64_t>());
+    mObjectPool->obtainFloat(mFuzzedDataProvider->ConsumeFloatingPoint<float>());
+    mObjectPool->obtainBoolean(mFuzzedDataProvider->ConsumeBool());
+    expectedValue->prop = toInt(kVehicleProp[vehicleProp2]);
+    expectedValue->areaId = 0;
+
+    mManager->set(*expectedValue.get());
+    invokeGet(toInt(kVehicleProp[vehicleProp2]), 0);
+    expectedValue->prop = toInt(kVehicleProp[vehicleProp3]);
+    mManager->set(*expectedValue.get());
+    expectedValue->prop = toInt(VehicleProperty::INVALID);
+    mManager->set(*expectedValue.get());
+}
+
+void VehicleHalManagerFuzzer::invokeObd2SensorStore() {
+    uint32_t diagnosticIntIndex =
+            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kIntSensorArrayLength - 1);
+    int32_t diagnosticIntValue = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    uint32_t diagnosticFloatIndex =
+            mFuzzedDataProvider->ConsumeIntegralInRange<uint32_t>(0, kFloatSensorArrayLength - 1);
+    float diagnosticFloatValue = mFuzzedDataProvider->ConsumeFloatingPoint<float>();
+
+    std::unique_ptr<Obd2SensorStore> sensorStore(
+            new Obd2SensorStore(kIntSensorArrayLength, kFloatSensorArrayLength));
+    if (sensorStore) {
+        sensorStore->setIntegerSensor(kDiagnosticIntIndex[diagnosticIntIndex], diagnosticIntValue);
+        sensorStore->setFloatSensor(kDiagnosticFloatIndex[diagnosticFloatIndex],
+                                    diagnosticFloatValue);
+        sensorStore->getIntegerSensors();
+        sensorStore->getFloatSensors();
+        sensorStore->getSensorsBitmask();
+        static std::vector<std::string> sampleDtcs = {"P0070",
+                                                      "P0102"
+                                                      "P0123"};
+        for (auto&& dtc : sampleDtcs) {
+            auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
+            sensorStore->fillPropValue(dtc, freezeFrame.get());
+            freezeFrame->prop = static_cast<int>(VehicleProperty::OBD2_FREEZE_FRAME);
+        }
+    }
+}
+
+void VehicleHalManagerFuzzer::invokeVmsUtils() {
+    bool availabilityMsgType = mFuzzedDataProvider->ConsumeBool();
+    bool subscriptionMsgType = mFuzzedDataProvider->ConsumeBool();
+    int32_t intValue = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+
+    VmsLayer layer(1, 0, 2);
+    auto message = createSubscribeMessage(layer);
+    isValidVmsMessage(*message);
+    message = createUnsubscribeMessage(layer);
+
+    VmsOffers offers = {intValue, {VmsLayerOffering(VmsLayer(1, 0, 2))}};
+    message = createOfferingMessage(offers);
+    std::vector<VmsLayer> dependencies = {VmsLayer(2, 0, 2), VmsLayer(3, 0, 3)};
+    std::vector<VmsLayerOffering> offering = {VmsLayerOffering(layer, dependencies)};
+    offers = {intValue, offering};
+    message = createOfferingMessage(offers);
+
+    message = createAvailabilityRequest();
+    message = createSubscriptionsRequest();
+
+    std::string bytes = "placeholder";
+    const VmsLayerAndPublisher layer_and_publisher(VmsLayer(2, 0, 1), intValue);
+    message = createDataMessageWithLayerPublisherInfo(layer_and_publisher, bytes);
+    parseData(*message);
+    createSubscribeToPublisherMessage(layer_and_publisher);
+    createUnsubscribeToPublisherMessage(layer_and_publisher);
+
+    std::string pub_bytes = "pub_id";
+    message = createPublisherIdRequest(pub_bytes);
+    message = createBaseVmsMessage(2);
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(VmsMessageType::PUBLISHER_ID_RESPONSE), intValue};
+    parsePublisherIdResponse(*message);
+
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(kSubscriptionMessageType[subscriptionMsgType]), intValue};
+    getSequenceNumberForSubscriptionsState(*message);
+
+    message->value.int32Values = hidl_vec<int32_t>{toInt(kSubscriptionMessageType[0]), intValue};
+    isSequenceNumberNewer(*message, intValue + 1);
+    invokeGetSubscribedLayers(kSubscriptionMessageType[subscriptionMsgType]);
+
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), 0};
+    hasServiceNewlyStarted(*message);
+    message = createStartSessionMessage(intValue, intValue + 1);
+    parseMessageType(*message);
+
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), intValue};
+    isAvailabilitySequenceNumberNewer(*message, intValue + 1);
+
+    message->value.int32Values =
+            hidl_vec<int32_t>{toInt(kAvailabilityMessageType[availabilityMsgType]), intValue};
+    getSequenceNumberForAvailabilityState(*message);
+    message = createBaseVmsMessage(3);
+    int new_service_id;
+    message->value.int32Values = hidl_vec<int32_t>{toInt(VmsMessageType::START_SESSION), 0, -1};
+    parseStartSessionMessage(*message, -1, 0, &new_service_id);
+}
+
+void VehicleHalManagerFuzzer::invokeGet(int32_t property, int32_t areaId) {
+    VehiclePropValue requestedValue{};
+    requestedValue.prop = property;
+    requestedValue.areaId = areaId;
+    mActualValue = VehiclePropValue{};  // reset previous values
+
+    StatusCode refStatus;
+    VehiclePropValue refValue;
+    mManager->get(requestedValue,
+                  [&refStatus, &refValue](StatusCode status, const VehiclePropValue& value) {
+                      refStatus = status;
+                      refValue = value;
+                  });
+
+    mActualValue = refValue;
+    mActualStatusCode = refStatus;
+}
+
+void VehicleHalManagerFuzzer::invokeGetSubscribedLayers(VmsMessageType type) {
+    VmsOffers offers = {123,
+                        {VmsLayerOffering(VmsLayer(1, 0, 1), {VmsLayer(4, 1, 1)}),
+                         VmsLayerOffering(VmsLayer(2, 0, 1))}};
+    auto message = createBaseVmsMessage(16);
+    message->value.int32Values = hidl_vec<int32_t>{toInt(type),
+                                                   1234,  // sequence number
+                                                   2,     // number of layers
+                                                   1,     // number of associated layers
+                                                   1,     // layer 1
+                                                   0,           1,
+                                                   4,  // layer 2
+                                                   1,           1,
+                                                   2,  // associated layer
+                                                   0,           1,
+                                                   2,    // number of publisher IDs
+                                                   111,  // publisher IDs
+                                                   123};
+    isValidVmsMessage(*message);
+    getSubscribedLayers(*message, offers);
+    getAvailableLayers(*message);
+}
+
+void VehicleHalManagerFuzzer::invokeVehiclePropStore() {
+    bool shouldWriteStatus = mFuzzedDataProvider->ConsumeBool();
+    int32_t vehicleProp = mFuzzedDataProvider->ConsumeIntegral<int32_t>();
+    auto store = std::make_unique<VehiclePropertyStore>();
+    VehiclePropConfig config{
+            .prop = vehicleProp,
+            .access = VehiclePropertyAccess::READ,
+            .changeMode = VehiclePropertyChangeMode::STATIC,
+            .areaConfigs = {VehicleAreaConfig{.areaId = (0)}},
+    };
+    store->registerProperty(config);
+    VehiclePropValue propValue{};
+    propValue.prop = vehicleProp;
+    propValue.areaId = 0;
+    store->writeValue(propValue, shouldWriteStatus);
+    store->readAllValues();
+    store->getAllConfigs();
+    store->getConfigOrNull(vehicleProp);
+    store->readValuesForProperty(vehicleProp);
+    store->readValueOrNull(propValue);
+    store->readValueOrNull(propValue.prop, propValue.areaId, 0);
+    store->removeValuesForProperty(vehicleProp);
+    store->removeValue(propValue);
+    store->getConfigOrDie(vehicleProp);
+}
+
+void VehicleHalManagerFuzzer::invokeWatchDogClient() {
+    auto service = new VehicleHalManager(mHal.get());
+    sp<Looper> looper(Looper::prepare(/*opts=*/mFuzzedDataProvider->ConsumeBool()));
+    if (auto watchdogClient = ndk::SharedRefBase::make<WatchdogClient>(looper, service);
+        watchdogClient->initialize()) {
+        watchdogClient->checkIfAlive(-1, TimeoutLength::TIMEOUT_NORMAL);
+        watchdogClient->prepareProcessTermination();
+    }
+    delete service;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    VehicleHalManagerFuzzer vmFuzzer;
+    vmFuzzer.process(data, size);
+    return 0;
+}
+
+}  // namespace android::hardware::automotive::vehicle::V2_0::fuzzer
diff --git a/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h
new file mode 100644
index 0000000..e9335d3
--- /dev/null
+++ b/automotive/vehicle/2.0/default/tests/fuzzer/VehicleManager_fuzzer.h
@@ -0,0 +1,124 @@
+/******************************************************************************
+ *
+ * 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.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#ifndef __VEHICLE_MANAGER_FUZZER_H__
+#define __VEHICLE_MANAGER_FUZZER_H__
+
+#include <vhal_v2_0/VehicleHalManager.h>
+#include <vhal_v2_0/VehiclePropertyStore.h>
+#include <vhal_v2_0/VmsUtils.h>
+
+#include <VehicleHalTestUtils.h>
+#include <fuzzer/FuzzedDataProvider.h>
+
+namespace android::hardware::automotive::vehicle::V2_0::fuzzer {
+
+constexpr int kRetriableAttempts = 3;
+
+class MockedVehicleHal : public VehicleHal {
+  public:
+    MockedVehicleHal()
+        : mFuelCapacityAttemptsLeft(kRetriableAttempts),
+          mMirrorFoldAttemptsLeft(kRetriableAttempts) {
+        mConfigs.assign(std::begin(kVehicleProperties), std::end(kVehicleProperties));
+    }
+
+    std::vector<VehiclePropConfig> listProperties() override { return mConfigs; }
+
+    VehiclePropValuePtr get(const VehiclePropValue& requestedPropValue,
+                            StatusCode* outStatus) override;
+
+    StatusCode set(const VehiclePropValue& propValue) override {
+        if (toInt(VehicleProperty::MIRROR_FOLD) == propValue.prop &&
+            mMirrorFoldAttemptsLeft-- > 0) {
+            return StatusCode::TRY_AGAIN;
+        }
+
+        mValues[makeKey(propValue)] = propValue;
+        return StatusCode::OK;
+    }
+
+    StatusCode subscribe(int32_t /* property */, float /* sampleRate */) override {
+        return StatusCode::OK;
+    }
+
+    StatusCode unsubscribe(int32_t /* property */) override { return StatusCode::OK; }
+
+    void sendPropEvent(recyclable_ptr<VehiclePropValue> value) { doHalEvent(std::move(value)); }
+
+    void sendHalError(StatusCode error, int32_t property, int32_t areaId) {
+        doHalPropertySetError(error, property, areaId);
+    }
+
+  private:
+    int64_t makeKey(const VehiclePropValue& v) const { return makeKey(v.prop, v.areaId); }
+
+    int64_t makeKey(int32_t prop, int32_t area) const {
+        return (static_cast<int64_t>(prop) << 32) | area;
+    }
+
+  private:
+    std::vector<VehiclePropConfig> mConfigs;
+    std::unordered_map<int64_t, VehiclePropValue> mValues;
+    int mFuelCapacityAttemptsLeft;
+    int mMirrorFoldAttemptsLeft;
+};
+
+class VehicleHalManagerFuzzer {
+  public:
+    VehicleHalManagerFuzzer() {
+        mHal.reset(new MockedVehicleHal);
+        mManager.reset(new VehicleHalManager(mHal.get()));
+        mObjectPool = mHal->getValuePool();
+    }
+    ~VehicleHalManagerFuzzer() {
+        mManager.reset(nullptr);
+        mHal.reset(nullptr);
+        mObjectPool = nullptr;
+        if (mFuzzedDataProvider) {
+            delete mFuzzedDataProvider;
+        }
+    }
+    void process(const uint8_t* data, size_t size);
+
+  private:
+    FuzzedDataProvider* mFuzzedDataProvider = nullptr;
+    VehiclePropValue mActualValue = VehiclePropValue{};
+    StatusCode mActualStatusCode = StatusCode::OK;
+
+    VehiclePropValuePool* mObjectPool = nullptr;
+    std::unique_ptr<MockedVehicleHal> mHal;
+    std::unique_ptr<VehicleHalManager> mManager;
+
+    void invokeDebug();
+    void invokePropConfigs();
+    void invokeSubscribe();
+    void invokeSetAndGetValues();
+    void invokeObd2SensorStore();
+    void invokeVmsUtils();
+    void invokeVehiclePropStore();
+    void invokeWatchDogClient();
+    void invokeGetSubscribedLayers(VmsMessageType type);
+    void invokeGet(int32_t property, int32_t areaId);
+};
+
+}  // namespace android::hardware::automotive::vehicle::V2_0::fuzzer
+
+#endif  // __VEHICLE_MANAGER_FUZZER_H__
diff --git a/automotive/vehicle/2.0/utils/Android.bp b/automotive/vehicle/2.0/utils/Android.bp
index e354634..a75ce49 100644
--- a/automotive/vehicle/2.0/utils/Android.bp
+++ b/automotive/vehicle/2.0/utils/Android.bp
@@ -13,6 +13,15 @@
 // limitations under the License.
 
 // User HAL helper library.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.automotive.vehicle@2.0-user-hal-helper-lib",
     defaults: ["vhal_v2_0_defaults"],
diff --git a/biometrics/face/1.0/Android.bp b/biometrics/face/1.0/Android.bp
index ebb8668..d3c262a 100644
--- a/biometrics/face/1.0/Android.bp
+++ b/biometrics/face/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.biometrics.face@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBiometricsFace.hal",
diff --git a/biometrics/face/1.0/default/Android.bp b/biometrics/face/1.0/default/Android.bp
index d6ff087..30cefed 100644
--- a/biometrics/face/1.0/default/Android.bp
+++ b/biometrics/face/1.0/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.biometrics.face@1.0-service.example",
     defaults: ["hidl_defaults"],
diff --git a/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc b/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
index 6c7362f..3fb827d 100644
--- a/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
+++ b/biometrics/face/1.0/default/android.hardware.biometrics.face@1.0-service.rc
@@ -5,6 +5,6 @@
     class late_start
     user system
     group system
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
     capabilities SYS_NICE
     rlimit rtprio 10 10
diff --git a/biometrics/face/1.0/vts/functional/Android.bp b/biometrics/face/1.0/vts/functional/Android.bp
index ff4a6de..259c4ec 100644
--- a/biometrics/face/1.0/vts/functional/Android.bp
+++ b/biometrics/face/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBiometricsFaceV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.biometrics.face@1.0"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
index 78f93af..379f37f 100644
--- a/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
+++ b/biometrics/face/1.0/vts/functional/VtsHalBiometricsFaceV1_0TargetTest.cpp
@@ -348,6 +348,7 @@
 
 }  // anonymous namespace
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FaceHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, FaceHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBiometricsFace::descriptor)),
diff --git a/biometrics/fingerprint/2.1/Android.bp b/biometrics/fingerprint/2.1/Android.bp
index c8cc0f1..3378930 100644
--- a/biometrics/fingerprint/2.1/Android.bp
+++ b/biometrics/fingerprint/2.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.biometrics.fingerprint@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBiometricsFingerprint.hal",
diff --git a/biometrics/fingerprint/2.1/default/Android.bp b/biometrics/fingerprint/2.1/default/Android.bp
index ec4838b..4d5cc8d 100644
--- a/biometrics/fingerprint/2.1/default/Android.bp
+++ b/biometrics/fingerprint/2.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.biometrics.fingerprint@2.1-service",
     defaults: ["hidl_defaults"],
diff --git a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
index 1667677..e7e8d30 100644
--- a/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
+++ b/biometrics/fingerprint/2.1/default/android.hardware.biometrics.fingerprint@2.1-service.rc
@@ -5,4 +5,4 @@
     class late_start
     user system
     group system input uhid
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/biometrics/fingerprint/2.1/vts/functional/Android.bp b/biometrics/fingerprint/2.1/vts/functional/Android.bp
index 7e3f340..0935bf6 100644
--- a/biometrics/fingerprint/2.1/vts/functional/Android.bp
+++ b/biometrics/fingerprint/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBiometricsFingerprintV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.biometrics.fingerprint@2.1"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
index 6093caa..b928314 100644
--- a/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
+++ b/biometrics/fingerprint/2.1/vts/functional/VtsHalBiometricsFingerprintV2_1TargetTest.cpp
@@ -472,6 +472,7 @@
 }
 }  // anonymous namespace
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FingerprintHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, FingerprintHidlTest,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  IBiometricsFingerprint::descriptor)),
diff --git a/biometrics/fingerprint/2.2/Android.bp b/biometrics/fingerprint/2.2/Android.bp
index 6c769ac..bea8d44 100644
--- a/biometrics/fingerprint/2.2/Android.bp
+++ b/biometrics/fingerprint/2.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.biometrics.fingerprint@2.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBiometricsFingerprint.hal",
diff --git a/biometrics/fingerprint/2.2/vts/functional/Android.bp b/biometrics/fingerprint/2.2/vts/functional/Android.bp
index 5e8e7c8..02f833a 100644
--- a/biometrics/fingerprint/2.2/vts/functional/Android.bp
+++ b/biometrics/fingerprint/2.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBiometricsFingerprintV2_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/bluetooth/1.0/Android.bp b/bluetooth/1.0/Android.bp
index 7036d6e..8d023c0 100644
--- a/bluetooth/1.0/Android.bp
+++ b/bluetooth/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.bluetooth@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBluetoothHci.hal",
diff --git a/bluetooth/1.0/default/Android.bp b/bluetooth/1.0/default/Android.bp
index f66c25e..ee368fd 100644
--- a/bluetooth/1.0/default/Android.bp
+++ b/bluetooth/1.0/default/Android.bp
@@ -13,7 +13,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_shared {
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
     name: "android.hardware.bluetooth@1.0-impl",
     defaults: ["hidl_defaults"],
     vendor: true,
@@ -102,13 +111,13 @@
     srcs: [
         "bluetooth_address.cc",
         "test/bluetooth_address_test.cc",
-        "test/properties.cc",
     ],
     local_include_dirs: [
         "test",
     ],
     shared_libs: [
         "libbase",
+        "libcutils",
         "liblog",
     ],
 }
diff --git a/bluetooth/1.0/default/h4_protocol.cc b/bluetooth/1.0/default/h4_protocol.cc
index 8c24f76..43abbe4 100644
--- a/bluetooth/1.0/default/h4_protocol.cc
+++ b/bluetooth/1.0/default/h4_protocol.cc
@@ -90,6 +90,7 @@
     hci_packet_type_ = static_cast<HciPacketType>(buffer[0]);
     if (hci_packet_type_ != HCI_PACKET_TYPE_ACL_DATA &&
         hci_packet_type_ != HCI_PACKET_TYPE_SCO_DATA &&
+        hci_packet_type_ != HCI_PACKET_TYPE_ISO_DATA &&
         hci_packet_type_ != HCI_PACKET_TYPE_EVENT) {
       LOG_ALWAYS_FATAL("%s: Unimplemented packet type %d", __func__,
                        static_cast<int>(hci_packet_type_));
diff --git a/bluetooth/1.0/default/hci_internals.h b/bluetooth/1.0/default/hci_internals.h
index 24e944f..6f7ff90 100644
--- a/bluetooth/1.0/default/hci_internals.h
+++ b/bluetooth/1.0/default/hci_internals.h
@@ -44,6 +44,10 @@
 const size_t HCI_EVENT_PREAMBLE_SIZE = 2;
 const size_t HCI_LENGTH_OFFSET_EVT = 1;
 
+// 2 bytes for handle and flags, 2 byte for data length (Volume 4, Part E, 5.4.5)
+const size_t HCI_ISO_PREAMBLE_SIZE = 4;
+const size_t HCI_LENGTH_OFFSET_ISO = 2;
+
 const size_t HCI_PREAMBLE_SIZE_MAX = HCI_ACL_PREAMBLE_SIZE;
 
 // Event codes (Volume 2, Part E, 7.7.14)
diff --git a/bluetooth/1.0/default/hci_packetizer.cc b/bluetooth/1.0/default/hci_packetizer.cc
index 7cb3a11..78ce61d 100644
--- a/bluetooth/1.0/default/hci_packetizer.cc
+++ b/bluetooth/1.0/default/hci_packetizer.cc
@@ -26,17 +26,27 @@
 
 namespace {
 
-const size_t preamble_size_for_type[] = {
-    0, HCI_COMMAND_PREAMBLE_SIZE, HCI_ACL_PREAMBLE_SIZE, HCI_SCO_PREAMBLE_SIZE,
-    HCI_EVENT_PREAMBLE_SIZE};
-const size_t packet_length_offset_for_type[] = {
-    0, HCI_LENGTH_OFFSET_CMD, HCI_LENGTH_OFFSET_ACL, HCI_LENGTH_OFFSET_SCO,
-    HCI_LENGTH_OFFSET_EVT};
+const size_t preamble_size_for_type[] = {0,
+                                         HCI_COMMAND_PREAMBLE_SIZE,
+                                         HCI_ACL_PREAMBLE_SIZE,
+                                         HCI_SCO_PREAMBLE_SIZE,
+                                         HCI_EVENT_PREAMBLE_SIZE,
+                                         HCI_ISO_PREAMBLE_SIZE};
+const size_t packet_length_offset_for_type[] = {0,
+                                                HCI_LENGTH_OFFSET_CMD,
+                                                HCI_LENGTH_OFFSET_ACL,
+                                                HCI_LENGTH_OFFSET_SCO,
+                                                HCI_LENGTH_OFFSET_EVT,
+                                                HCI_LENGTH_OFFSET_ISO};
 
 size_t HciGetPacketLengthForType(HciPacketType type, const uint8_t* preamble) {
   size_t offset = packet_length_offset_for_type[type];
-  if (type != HCI_PACKET_TYPE_ACL_DATA) return preamble[offset];
-  return (((preamble[offset + 1]) << 8) | preamble[offset]);
+  if (type == HCI_PACKET_TYPE_ACL_DATA) {
+    return (((preamble[offset + 1]) << 8) | preamble[offset]);
+  } else if (type == HCI_PACKET_TYPE_ISO_DATA) {
+    return ((((preamble[offset + 1]) & 0x3f) << 8) | preamble[offset]);
+  }
+  return preamble[offset];
 }
 
 }  // namespace
diff --git a/bluetooth/1.0/default/test/bluetooth_address_test.cc b/bluetooth/1.0/default/test/bluetooth_address_test.cc
index 2c8dbe5..422d6a3 100644
--- a/bluetooth/1.0/default/test/bluetooth_address_test.cc
+++ b/bluetooth/1.0/default/test/bluetooth_address_test.cc
@@ -14,7 +14,6 @@
 // limitations under the License.
 //
 
-#include <cutils/properties.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <gtest/gtest.h>
@@ -39,12 +38,6 @@
     "00:00:00:00:00:00";
 constexpr uint8_t kZeros_bytes[BluetoothAddress::kBytes] = {0x00, 0x00, 0x00,
                                                             0x00, 0x00, 0x00};
-constexpr char kTestAddrBad1[BluetoothAddress::kStringLength + 1] =
-    "bb:aa:dd:00:00:01";
-constexpr uint8_t kTestAddrBad1_bytes[BluetoothAddress::kBytes] = {
-    0xbb, 0xaa, 0xdd, 0x00, 0x00, 0x01};
-
-constexpr char kAddrPath[] = "/tmp/my_address_in_a_file.txt";
 
 class BluetoothAddressTest : public ::testing::Test {
  public:
@@ -120,134 +113,6 @@
   EXPECT_FALSE(memcmp(addrA, addrB, BluetoothAddress::kStringLength) == 0);
 }
 
-TEST_F(BluetoothAddressTest, property_set) {
-  // Set the properties to empty strings.
-  property_set(PERSIST_BDADDR_PROPERTY, "");
-  property_set(PROPERTY_BT_BDADDR_PATH, "");
-  property_set(FACTORY_BDADDR_PROPERTY, "");
-
-  // Get returns 0.
-  char prop[PROP_VALUE_MAX] = "";
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) == 0);
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) == 0);
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) == 0);
-
-  // Set the properties to known strings.
-  property_set(PERSIST_BDADDR_PROPERTY, "1");
-  property_set(PROPERTY_BT_BDADDR_PATH, "22");
-  property_set(FACTORY_BDADDR_PROPERTY, "333");
-
-  // Get returns the correct length.
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) == 1);
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) == 2);
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) == 3);
-
-  // Set the properties to empty strings again.
-  property_set(PERSIST_BDADDR_PROPERTY, "");
-  property_set(PROPERTY_BT_BDADDR_PATH, "");
-  property_set(FACTORY_BDADDR_PROPERTY, "");
-
-  // Get returns 0.
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) == 0);
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) == 0);
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) == 0);
-}
-
-TEST_F(BluetoothAddressTest, property_get) {
-  // Set the properties to known strings.
-  property_set(PERSIST_BDADDR_PROPERTY, PERSIST_BDADDR_PROPERTY);
-  property_set(PROPERTY_BT_BDADDR_PATH, PROPERTY_BT_BDADDR_PATH);
-  property_set(FACTORY_BDADDR_PROPERTY, FACTORY_BDADDR_PROPERTY);
-
-  // Get returns the same strings.
-  char prop[PROP_VALUE_MAX] = "";
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(PERSIST_BDADDR_PROPERTY, prop) == 0);
-
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(PROPERTY_BT_BDADDR_PATH, prop) == 0);
-
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(FACTORY_BDADDR_PROPERTY, prop) == 0);
-
-  // Set a property to a different known string.
-  char prop2[PROP_VALUE_MAX] = "Erased";
-  property_set(PERSIST_BDADDR_PROPERTY, prop2);
-
-  // Get returns the correct strings.
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(PROPERTY_BT_BDADDR_PATH, prop) == 0);
-
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(FACTORY_BDADDR_PROPERTY, prop) == 0);
-
-  // Set another property to prop2.
-  property_set(PROPERTY_BT_BDADDR_PATH, prop2);
-
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(FACTORY_BDADDR_PROPERTY, prop) == 0);
-
-  // Set the third property to prop2.
-  property_set(FACTORY_BDADDR_PROPERTY, prop2);
-
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-
-  EXPECT_TRUE(property_get(PROPERTY_BT_BDADDR_PATH, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-
-  EXPECT_TRUE(property_get(FACTORY_BDADDR_PROPERTY, prop, NULL) > 0);
-  EXPECT_TRUE(strcmp(prop2, prop) == 0);
-}
-
-TEST_F(BluetoothAddressTest, get_local_address) {
-  EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, "") == 0);
-  EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, "") == 0);
-  uint8_t address[BluetoothAddress::kBytes];
-
-  // File contains a non-zero Address.
-  FileWriteString(kAddrPath, kTestAddr1);
-  EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, kAddrPath) == 0);
-  EXPECT_TRUE(BluetoothAddress::get_local_address(address));
-  EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-
-  // File contains a zero address.  A random address will be generated.
-  FileWriteString(kAddrPath, kZeros);
-  EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, kAddrPath) == 0);
-  EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddrBad1) == 0);
-  EXPECT_TRUE(BluetoothAddress::get_local_address(address));
-  EXPECT_TRUE(memcmp(address, kZeros_bytes, BluetoothAddress::kBytes) != 0);
-  char prop[PROP_VALUE_MAX] = "Before reading";
-  EXPECT_TRUE(property_get(PERSIST_BDADDR_PROPERTY, prop, NULL) ==
-              BluetoothAddress::kStringLength);
-  char address_str[BluetoothAddress::kStringLength + 1];
-  BluetoothAddress::bytes_to_string(address, address_str);
-  EXPECT_TRUE(memcmp(address_str, prop, BluetoothAddress::kStringLength) == 0);
-
-  // Factory property contains an address.
-  EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddrBad1) == 0);
-  EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, kTestAddr1) == 0);
-  EXPECT_TRUE(BluetoothAddress::get_local_address(address));
-  EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-
-  // Persistent property contains an address.
-  memcpy(address, kTestAddrBad1_bytes, BluetoothAddress::kBytes);
-  EXPECT_TRUE(property_set(PERSIST_BDADDR_PROPERTY, kTestAddr1) == 0);
-  EXPECT_TRUE(property_set(FACTORY_BDADDR_PROPERTY, "") == 0);
-  EXPECT_TRUE(property_set(PROPERTY_BT_BDADDR_PATH, "") == 0);
-  EXPECT_TRUE(BluetoothAddress::get_local_address(address));
-  EXPECT_TRUE(memcmp(address, kTestAddr1_bytes, BluetoothAddress::kBytes) == 0);
-}
-
 }  // namespace implementation
 }  // namespace V1_0
 }  // namespace bluetooth
diff --git a/bluetooth/1.0/default/test/h4_protocol_unittest.cc b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
index 283243d..174861c 100644
--- a/bluetooth/1.0/default/test/h4_protocol_unittest.cc
+++ b/bluetooth/1.0/default/test/h4_protocol_unittest.cc
@@ -190,8 +190,10 @@
 
   void WriteAndExpectInboundIsoData(char* payload) {
     // h4 type[1] + handle[2] + size[1]
-    char preamble[4] = {HCI_PACKET_TYPE_ISO_DATA, 20, 17, 0};
-    preamble[3] = strlen(payload) & 0xFF;
+    char preamble[5] = {HCI_PACKET_TYPE_ISO_DATA, 19, 92, 0, 0};
+    int length = strlen(payload);
+    preamble[3] = length & 0xFF;
+    preamble[4] = (length >> 8) & 0x3F;
 
     ALOGD("%s writing", __func__);
     TEMP_FAILURE_RETRY(write(fake_uart_, preamble, sizeof(preamble)));
diff --git a/bluetooth/1.0/default/test/properties.cc b/bluetooth/1.0/default/test/properties.cc
deleted file mode 100644
index 70de01e..0000000
--- a/bluetooth/1.0/default/test/properties.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-//
-// Copyright 2016 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.
-//
-
-#define LOG_TAG "properties"
-
-#include <ctype.h>
-#include <stdbool.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <cutils/properties.h>
-#include <log/log.h>
-
-static const int MAX_PROPERTIES = 5;
-
-struct property {
-  char key[PROP_KEY_MAX + 2];
-  char value[PROP_VALUE_MAX + 2];
-};
-
-int num_properties = 0;
-struct property properties[MAX_PROPERTIES];
-
-// Find the correct entry.
-static int property_find(const char* key) {
-  for (int i = 0; i < num_properties; i++) {
-    if (strncmp(properties[i].key, key, PROP_KEY_MAX) == 0) {
-      return i;
-    }
-  }
-  return MAX_PROPERTIES;
-}
-
-int property_set(const char* key, const char* value) {
-  if (strnlen(value, PROP_VALUE_MAX) > PROP_VALUE_MAX) return -1;
-
-  // Check to see if the property exists.
-  int prop_index = property_find(key);
-
-  if (prop_index == MAX_PROPERTIES) {
-    if (num_properties >= MAX_PROPERTIES) return -1;
-    prop_index = num_properties;
-    num_properties += 1;
-  }
-
-  // This is test code.  Be nice and don't push the boundary cases!
-  strncpy(properties[prop_index].key, key, PROP_KEY_MAX + 1);
-  strncpy(properties[prop_index].value, value, PROP_VALUE_MAX + 1);
-  return 0;
-}
-
-int property_get(const char* key, char* value, const char* default_value) {
-  // This doesn't mock the behavior of default value
-  if (default_value != NULL) ALOGE("%s: default_value is ignored!", __func__);
-
-  // Check to see if the property exists.
-  int prop_index = property_find(key);
-
-  if (prop_index == MAX_PROPERTIES) return 0;
-
-  int len = strlen(properties[prop_index].value);
-  memcpy(value, properties[prop_index].value, len);
-  value[len] = '\0';
-  return len;
-}
diff --git a/bluetooth/1.0/default/test/sys/system_properties.h b/bluetooth/1.0/default/test/sys/system_properties.h
deleted file mode 100644
index b477a6b..0000000
--- a/bluetooth/1.0/default/test/sys/system_properties.h
+++ /dev/null
@@ -1,20 +0,0 @@
-//
-// Copyright 2016 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.
-//
-
-// Mock sys/system_properties.h for testing
-
-#define PROP_VALUE_MAX 50
-#define PROP_KEY_MAX 50
diff --git a/bluetooth/1.0/vts/functional/Android.bp b/bluetooth/1.0/vts/functional/Android.bp
index e9f867f..768142c 100644
--- a/bluetooth/1.0/vts/functional/Android.bp
+++ b/bluetooth/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBluetoothV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -22,6 +31,7 @@
         "android.hardware.bluetooth@1.0",
         "libbluetooth-types",
     ],
+    test_config: "VtsHalBluetoothV1_0TargetTest.xml",
     test_suites: [
         "general-tests",
         "vts",
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
index ef02eff..fd82f49 100644
--- a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.cpp
@@ -179,7 +179,7 @@
     bluetooth_cb->SetWaitTimeout(kCallbackNameScoEventReceived,
                                  WAIT_FOR_SCO_DATA_TIMEOUT);
 
-    EXPECT_TRUE(
+    ASSERT_TRUE(
         bluetooth_cb->WaitForCallback(kCallbackNameInitializationComplete)
             .no_timeout);
 
@@ -289,7 +289,7 @@
 void BluetoothHidlTest::handle_no_ops() {
   while (event_queue.size() > 0) {
     hidl_vec<uint8_t> event = event_queue.front();
-    EXPECT_GE(event.size(),
+    ASSERT_GE(event.size(),
               static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
     bool event_is_no_op =
         (event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) &&
@@ -327,7 +327,7 @@
         bluetooth_cb->WaitForCallback(kCallbackNameHciEventReceived).no_timeout;
     EXPECT_TRUE(no_timeout || !timeout_is_error);
     if (no_timeout && timeout_is_error) {
-      EXPECT_LT(static_cast<size_t>(0), event_queue.size());
+      ASSERT_LT(static_cast<size_t>(0), event_queue.size());
     }
     if (event_queue.size() == 0) {
       // WaitForCallback timed out.
@@ -343,12 +343,12 @@
   hidl_vec<uint8_t> event = event_queue.front();
   event_queue.pop();
 
-  EXPECT_GT(event.size(),
+  ASSERT_GT(event.size(),
             static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
-  EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
-  EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
-  EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
-  EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+  ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+  ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+  ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+  ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
 }
 
 // Send the command to read the controller's buffer sizes.
@@ -362,10 +362,10 @@
   hidl_vec<uint8_t> event = event_queue.front();
   event_queue.pop();
 
-  EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
-  EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
-  EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
-  EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+  ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+  ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+  ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+  ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
 
   max_acl_data_packet_length =
       event[EVENT_COMMAND_COMPLETE_STATUS_BYTE + 1] +
@@ -415,10 +415,10 @@
     size_t compare_length =
         (cmd.size() > static_cast<size_t>(0xff) ? static_cast<size_t>(0xff)
                                                 : cmd.size());
-    EXPECT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
+    ASSERT_GT(event.size(), compare_length + EVENT_FIRST_PAYLOAD_BYTE - 1);
 
-    EXPECT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
-    EXPECT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
+    ASSERT_EQ(EVENT_LOOPBACK_COMMAND, event[EVENT_CODE_BYTE]);
+    ASSERT_EQ(compare_length, event[EVENT_LENGTH_BYTE]);
 
     // Don't compare past the end of the event.
     if (compare_length + EVENT_FIRST_PAYLOAD_BYTE > event.size()) {
@@ -455,12 +455,12 @@
     bluetooth->sendScoData(sco_vector);
 
     // Check the loopback of the SCO packet
-    EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
+    ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameScoEventReceived)
                     .no_timeout);
     hidl_vec<uint8_t> sco_loopback = sco_queue.front();
     sco_queue.pop();
 
-    EXPECT_EQ(sco_packet.size(), sco_loopback.size());
+    ASSERT_EQ(sco_packet.size(), sco_loopback.size());
     size_t successful_bytes = 0;
 
     for (size_t i = 0; i < sco_packet.size(); i++) {
@@ -474,7 +474,7 @@
         break;
       }
     }
-    EXPECT_EQ(sco_packet.size(), successful_bytes + 1);
+    ASSERT_EQ(sco_packet.size(), successful_bytes + 1);
   }
   logger.setTotalBytes(num_packets * size * 2);
 }
@@ -500,26 +500,15 @@
     bluetooth->sendAclData(acl_vector);
 
     // Check the loopback of the ACL packet
-    EXPECT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
+    ASSERT_TRUE(bluetooth_cb->WaitForCallback(kCallbackNameAclEventReceived)
                     .no_timeout);
     hidl_vec<uint8_t> acl_loopback = acl_queue.front();
     acl_queue.pop();
 
     EXPECT_EQ(acl_packet.size(), acl_loopback.size());
-    size_t successful_bytes = 0;
-
-    for (size_t i = 0; i < acl_packet.size(); i++) {
-      if (acl_packet[i] == acl_loopback[i]) {
-        successful_bytes = i;
-      } else {
-        ALOGE("Miscompare at %d (expected %x, got %x)", static_cast<int>(i),
-              acl_packet[i], acl_loopback[i]);
-        ALOGE("At %d (expected %x, got %x)", static_cast<int>(i + 1),
-              acl_packet[i + 1], acl_loopback[i + 1]);
-        break;
-      }
+    for (size_t i = 0; i < acl_packet.size() && i < acl_loopback.size(); i++) {
+      EXPECT_EQ(acl_packet[i], acl_loopback[i]) << " at byte number " << i;
     }
-    EXPECT_EQ(acl_packet.size(), successful_bytes + 1);
   }
   logger.setTotalBytes(num_packets * size * 2);
 }
@@ -560,22 +549,22 @@
     wait_for_event(false);
     if (event_queue.size() == 0) {
       // Fail if there was no event received or no connections completed.
-      EXPECT_TRUE(command_complete_received);
-      EXPECT_LT(0, connection_event_count);
+      ASSERT_TRUE(command_complete_received);
+      ASSERT_LT(0, connection_event_count);
       return;
     }
     hidl_vec<uint8_t> event = event_queue.front();
     event_queue.pop();
-    EXPECT_GT(event.size(),
+    ASSERT_GT(event.size(),
               static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
     if (event[EVENT_CODE_BYTE] == EVENT_CONNECTION_COMPLETE) {
-      EXPECT_GT(event.size(),
+      ASSERT_GT(event.size(),
                 static_cast<size_t>(EVENT_CONNECTION_COMPLETE_TYPE));
-      EXPECT_EQ(event[EVENT_LENGTH_BYTE],
+      ASSERT_EQ(event[EVENT_LENGTH_BYTE],
                 EVENT_CONNECTION_COMPLETE_PARAM_LENGTH);
       uint8_t connection_type = event[EVENT_CONNECTION_COMPLETE_TYPE];
 
-      EXPECT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
+      ASSERT_TRUE(connection_type == EVENT_CONNECTION_COMPLETE_TYPE_SCO ||
                   connection_type == EVENT_CONNECTION_COMPLETE_TYPE_ACL);
 
       // Save handles
@@ -590,10 +579,10 @@
             event[EVENT_CONNECTION_COMPLETE_TYPE], handle);
       connection_event_count++;
     } else {
-      EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
-      EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
-      EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
-      EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+      ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+      ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+      ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+      ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
       command_complete_received = true;
     }
   }
@@ -620,15 +609,15 @@
 
   hidl_vec<uint8_t> event = event_queue.front();
   event_queue.pop();
-  EXPECT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
+  ASSERT_GT(event.size(), static_cast<size_t>(EVENT_LOCAL_LMP_VERSION_BYTE));
 
-  EXPECT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
-  EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
-  EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
-  EXPECT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
+  ASSERT_EQ(EVENT_COMMAND_COMPLETE, event[EVENT_CODE_BYTE]);
+  ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+  ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+  ASSERT_EQ(HCI_STATUS_SUCCESS, event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
 
-  EXPECT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
-  EXPECT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
+  ASSERT_LE(HCI_MINIMUM_HCI_VERSION, event[EVENT_LOCAL_HCI_VERSION_BYTE]);
+  ASSERT_LE(HCI_MINIMUM_LMP_VERSION, event[EVENT_LOCAL_LMP_VERSION_BYTE]);
 }
 
 // Send an unknown HCI command and wait for the error message.
@@ -642,18 +631,18 @@
   hidl_vec<uint8_t> event = event_queue.front();
   event_queue.pop();
 
-  EXPECT_GT(event.size(),
+  ASSERT_GT(event.size(),
             static_cast<size_t>(EVENT_COMMAND_COMPLETE_STATUS_BYTE));
   if (event[EVENT_CODE_BYTE] == EVENT_COMMAND_COMPLETE) {
-    EXPECT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
-    EXPECT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
-    EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+    ASSERT_EQ(cmd[0], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE]);
+    ASSERT_EQ(cmd[1], event[EVENT_COMMAND_COMPLETE_OPCODE_LSBYTE + 1]);
+    ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
               event[EVENT_COMMAND_COMPLETE_STATUS_BYTE]);
   } else {
-    EXPECT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
-    EXPECT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
-    EXPECT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
-    EXPECT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
+    ASSERT_EQ(EVENT_COMMAND_STATUS, event[EVENT_CODE_BYTE]);
+    ASSERT_EQ(cmd[0], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE]);
+    ASSERT_EQ(cmd[1], event[EVENT_COMMAND_STATUS_OPCODE_LSBYTE + 1]);
+    ASSERT_EQ(HCI_STATUS_UNKNOWN_HCI_COMMAND,
               event[EVENT_COMMAND_STATUS_STATUS_BYTE]);
   }
 }
@@ -678,7 +667,7 @@
   // This should work, but breaks on some current platforms.  Figure out how to
   // grandfather older devices but test new ones.
   if (0 && sco_connection_handles.size() > 0) {
-    EXPECT_LT(0, max_sco_data_packet_length);
+    ASSERT_LT(0, max_sco_data_packet_length);
     sendAndCheckSCO(1, max_sco_data_packet_length, sco_connection_handles[0]);
     int sco_packets_sent = 1;
     int completed_packets =
@@ -690,7 +679,7 @@
   }
 
   if (acl_connection_handles.size() > 0) {
-    EXPECT_LT(0, max_acl_data_packet_length);
+    ASSERT_LT(0, max_acl_data_packet_length);
     sendAndCheckACL(1, max_acl_data_packet_length, acl_connection_handles[0]);
     int acl_packets_sent = 1;
     int completed_packets =
@@ -715,7 +704,7 @@
   // This should work, but breaks on some current platforms.  Figure out how to
   // grandfather older devices but test new ones.
   if (0 && sco_connection_handles.size() > 0) {
-    EXPECT_LT(0, max_sco_data_packet_length);
+    ASSERT_LT(0, max_sco_data_packet_length);
     sendAndCheckSCO(NUM_SCO_PACKETS_BANDWIDTH, max_sco_data_packet_length,
                     sco_connection_handles[0]);
     int sco_packets_sent = NUM_SCO_PACKETS_BANDWIDTH;
@@ -728,7 +717,7 @@
   }
 
   if (acl_connection_handles.size() > 0) {
-    EXPECT_LT(0, max_acl_data_packet_length);
+    ASSERT_LT(0, max_acl_data_packet_length);
     sendAndCheckACL(NUM_ACL_PACKETS_BANDWIDTH, max_acl_data_packet_length,
                     acl_connection_handles[0]);
     int acl_packets_sent = NUM_ACL_PACKETS_BANDWIDTH;
@@ -741,6 +730,7 @@
   }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothHidlTest);
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, BluetoothHidlTest,
     testing::ValuesIn(
diff --git a/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
new file mode 100644
index 0000000..09463c9
--- /dev/null
+++ b/bluetooth/1.0/vts/functional/VtsHalBluetoothV1_0TargetTest.xml
@@ -0,0 +1,38 @@
+<?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 VtsHalBluetoothV1_0TargetTest.">
+    <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.DeviceSetup">
+        <option name="bluetooth" value="off" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalBluetoothV1_0TargetTest->/data/local/tmp/VtsHalBluetoothV1_0TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalBluetoothV1_0TargetTest" />
+    </test>
+</configuration>
diff --git a/bluetooth/1.1/Android.bp b/bluetooth/1.1/Android.bp
index 4204aed..4feb28b 100644
--- a/bluetooth/1.1/Android.bp
+++ b/bluetooth/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.bluetooth@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IBluetoothHci.hal",
         "IBluetoothHciCallbacks.hal",
diff --git a/bluetooth/1.1/default/Android.bp b/bluetooth/1.1/default/Android.bp
index 4f7fecb..ce85db0 100644
--- a/bluetooth/1.1/default/Android.bp
+++ b/bluetooth/1.1/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.bluetooth@1.1-service",
     defaults: ["hidl_defaults"],
diff --git a/bluetooth/1.1/vts/functional/Android.bp b/bluetooth/1.1/vts/functional/Android.bp
index eb4a720..7f56647 100644
--- a/bluetooth/1.1/vts/functional/Android.bp
+++ b/bluetooth/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBluetoothV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -23,5 +32,6 @@
         "android.hardware.bluetooth@1.0",
         "libbluetooth-types",
     ],
+    test_config: "VtsHalBluetoothV1_1TargetTest.xml",
     test_suites: ["general-tests", "vts"],
 }
diff --git a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
index 659b2c8..9ae3837 100644
--- a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
+++ b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.cpp
@@ -753,6 +753,7 @@
   }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothHidlTest);
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, BluetoothHidlTest,
     testing::ValuesIn(
diff --git a/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.xml
new file mode 100644
index 0000000..d64751a
--- /dev/null
+++ b/bluetooth/1.1/vts/functional/VtsHalBluetoothV1_1TargetTest.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 VtsHalBluetoothV1_1TargetTest.">
+    <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.DeviceSetup">
+        <option name="bluetooth" value="off" />
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalBluetoothV1_1TargetTest->/data/local/tmp/VtsHalBluetoothV1_1TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalBluetoothV1_1TargetTest" />
+    </test>
+</configuration>
diff --git a/bluetooth/a2dp/1.0/Android.bp b/bluetooth/a2dp/1.0/Android.bp
index 02f224a..7f39d4f 100644
--- a/bluetooth/a2dp/1.0/Android.bp
+++ b/bluetooth/a2dp/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.bluetooth.a2dp@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBluetoothAudioHost.hal",
diff --git a/bluetooth/a2dp/1.0/default/Android.bp b/bluetooth/a2dp/1.0/default/Android.bp
index 5264899..f368dd4 100644
--- a/bluetooth/a2dp/1.0/default/Android.bp
+++ b/bluetooth/a2dp/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.bluetooth.a2dp@1.0-impl.mock",
     relative_install_path: "hw",
diff --git a/bluetooth/a2dp/1.0/vts/functional/Android.bp b/bluetooth/a2dp/1.0/vts/functional/Android.bp
index df18fcc..0d393bc 100644
--- a/bluetooth/a2dp/1.0/vts/functional/Android.bp
+++ b/bluetooth/a2dp/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBluetoothA2dpV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/bluetooth/a2dp/1.0/vts/functional/VtsHalBluetoothA2dpV1_0TargetTest.cpp b/bluetooth/a2dp/1.0/vts/functional/VtsHalBluetoothA2dpV1_0TargetTest.cpp
index 44b138a..f7fdf31 100644
--- a/bluetooth/a2dp/1.0/vts/functional/VtsHalBluetoothA2dpV1_0TargetTest.cpp
+++ b/bluetooth/a2dp/1.0/vts/functional/VtsHalBluetoothA2dpV1_0TargetTest.cpp
@@ -107,4 +107,6 @@
     PerInstance, BluetoothA2dpHidlTest,
     testing::ValuesIn(android::hardware::getAllHalInstanceNames(
         IBluetoothAudioOffload::descriptor)),
-    android::hardware::PrintInstanceNameToString);
\ No newline at end of file
+    android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BluetoothA2dpHidlTest);
diff --git a/bluetooth/audio/2.0/Android.bp b/bluetooth/audio/2.0/Android.bp
index 6bf0070..dd39f9f 100644
--- a/bluetooth/audio/2.0/Android.bp
+++ b/bluetooth/audio/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.bluetooth.audio@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBluetoothAudioPort.hal",
diff --git a/bluetooth/audio/2.0/default/Android.bp b/bluetooth/audio/2.0/default/Android.bp
index 0db0028..9bca0ca 100644
--- a/bluetooth/audio/2.0/default/Android.bp
+++ b/bluetooth/audio/2.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.bluetooth.audio@2.0-impl",
     defaults: ["hidl_defaults"],
@@ -23,24 +32,3 @@
         "libutils",
     ],
 }
-
-cc_library_shared {
-    name: "libbluetooth_audio_session",
-    defaults: ["hidl_defaults"],
-    vendor: true,
-    srcs: [
-        "session/BluetoothAudioSession.cpp",
-        "session/BluetoothAudioSupportedCodecsDB.cpp",
-    ],
-    export_include_dirs: ["session/"],
-    header_libs: ["libhardware_headers"],
-    shared_libs: [
-        "android.hardware.bluetooth.audio@2.0",
-        "libbase",
-        "libcutils",
-        "libfmq",
-        "libhidlbase",
-        "liblog",
-        "libutils",
-    ],
-}
diff --git a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
index df89cc8..0e5b7e4 100644
--- a/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
+++ b/bluetooth/audio/2.0/default/BluetoothAudioProvidersFactory.cpp
@@ -75,7 +75,7 @@
         android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
     if (db_codec_capabilities.size()) {
       audio_capabilities.resize(db_codec_capabilities.size());
-      for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+      for (std::size_t i = 0; i < db_codec_capabilities.size(); ++i) {
         audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
       }
     }
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
deleted file mode 100644
index d60e732..0000000
--- a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.cpp
+++ /dev/null
@@ -1,427 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-#define LOG_TAG "BTAudioProviderSession"
-
-#include "BluetoothAudioSession.h"
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-using ::android::hardware::audio::common::V5_0::AudioContentType;
-using ::android::hardware::audio::common::V5_0::AudioUsage;
-using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
-using ::android::hardware::audio::common::V5_0::SourceMetadata;
-using ::android::hardware::bluetooth::audio::V2_0::CodecType;
-using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
-
-const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
-    .codecType = CodecType::UNKNOWN,
-    .encodedAudioBitrate = 0x00000000,
-    .peerMtu = 0xffff,
-    .isScmstEnabled = false,
-    .config = {}};
-AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
-    {};
-AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
-
-static constexpr int kFmqSendTimeoutMs = 1000;  // 1000 ms timeout for sending
-static constexpr int kWritePollMs = 1;          // polled non-blocking interval
-
-static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
-  return {.tv_sec = static_cast<long>(TS.tvSec),
-          .tv_nsec = static_cast<long>(TS.tvNSec)};
-}
-
-BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
-    : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
-  invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
-  invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
-}
-
-// The report function is used to report that the Bluetooth stack has started
-// this session without any failure, and will invoke session_changed_cb_ to
-// notify those registered bluetooth_audio outputs
-void BluetoothAudioSession::OnSessionStarted(
-    const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
-    const AudioConfiguration& audio_config) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (stack_iface == nullptr) {
-    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
-               << ", IBluetoothAudioPort Invalid";
-  } else if (!UpdateAudioConfig(audio_config)) {
-    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
-               << ", AudioConfiguration=" << toString(audio_config)
-               << " Invalid";
-  } else if (!UpdateDataPath(dataMQ)) {
-    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
-               << " DataMQ Invalid";
-    audio_config_ =
-        (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
-             ? kInvalidOffloadAudioConfiguration
-             : kInvalidSoftwareAudioConfiguration);
-  } else {
-    stack_iface_ = stack_iface;
-    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
-              << ", AudioConfiguration=" << toString(audio_config);
-    ReportSessionStatus();
-  }
-}
-
-// The report function is used to report that the Bluetooth stack has ended the
-// session, and will invoke session_changed_cb_ to notify registered
-// bluetooth_audio outputs
-void BluetoothAudioSession::OnSessionEnded() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (IsSessionReady()) {
-    ReportSessionStatus();
-  }
-  audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
-                       ? kInvalidOffloadAudioConfiguration
-                       : kInvalidSoftwareAudioConfiguration);
-  stack_iface_ = nullptr;
-  UpdateDataPath(nullptr);
-}
-
-// invoking the registered session_changed_cb_
-void BluetoothAudioSession::ReportSessionStatus() {
-  // This is locked already by OnSessionStarted / OnSessionEnded
-  if (observers_.empty()) {
-    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
-              << " has NO port state observer";
-    return;
-  }
-  for (auto& observer : observers_) {
-    uint16_t cookie = observer.first;
-    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
-    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
-              << " notify to bluetooth_audio=0x"
-              << android::base::StringPrintf("%04x", cookie);
-    cb->session_changed_cb_(cookie);
-  }
-}
-
-// The report function is used to report that the Bluetooth stack has notified
-// the result of startStream or suspendStream, and will invoke
-// control_result_cb_ to notify registered bluetooth_audio outputs
-void BluetoothAudioSession::ReportControlStatus(
-    bool start_resp, const BluetoothAudioStatus& status) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (observers_.empty()) {
-    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
-                 << " has NO port state observer";
-    return;
-  }
-  for (auto& observer : observers_) {
-    uint16_t cookie = observer.first;
-    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
-    LOG(INFO) << __func__ << " - status=" << toString(status)
-              << " for SessionType=" << toString(session_type_)
-              << ", bluetooth_audio=0x"
-              << android::base::StringPrintf("%04x", cookie)
-              << (start_resp ? " started" : " suspended");
-    cb->control_result_cb_(cookie, start_resp, status);
-  }
-}
-
-// The function helps to check if this session is ready or not
-// @return: true if the Bluetooth stack has started the specified session
-bool BluetoothAudioSession::IsSessionReady() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  bool dataMQ_valid =
-      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
-       (mDataMQ != nullptr && mDataMQ->isValid()));
-  return stack_iface_ != nullptr && dataMQ_valid;
-}
-
-bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
-  if (dataMQ == nullptr) {
-    // usecase of reset by nullptr
-    mDataMQ = nullptr;
-    return true;
-  }
-  std::unique_ptr<DataMQ> tempDataMQ;
-  tempDataMQ.reset(new DataMQ(*dataMQ));
-  if (!tempDataMQ || !tempDataMQ->isValid()) {
-    mDataMQ = nullptr;
-    return false;
-  }
-  mDataMQ = std::move(tempDataMQ);
-  return true;
-}
-
-bool BluetoothAudioSession::UpdateAudioConfig(
-    const AudioConfiguration& audio_config) {
-  bool is_software_session =
-      (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
-       session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
-  bool is_offload_session =
-      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
-  auto audio_config_discriminator = audio_config.getDiscriminator();
-  bool is_software_audio_config =
-      (is_software_session &&
-       audio_config_discriminator ==
-           AudioConfiguration::hidl_discriminator::pcmConfig);
-  bool is_offload_audio_config =
-      (is_offload_session &&
-       audio_config_discriminator ==
-           AudioConfiguration::hidl_discriminator::codecConfig);
-  if (!is_software_audio_config && !is_offload_audio_config) {
-    return false;
-  }
-  audio_config_ = audio_config;
-  return true;
-}
-
-// The control function helps the bluetooth_audio module to register
-// PortStatusCallbacks
-// @return: cookie - the assigned number to this bluetooth_audio output
-uint16_t BluetoothAudioSession::RegisterStatusCback(
-    const PortStatusCallbacks& cbacks) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  uint16_t cookie = ObserversCookieGetInitValue(session_type_);
-  uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
-
-  while (cookie < cookie_upper_bound) {
-    if (observers_.find(cookie) == observers_.end()) {
-      break;
-    }
-    ++cookie;
-  }
-  if (cookie >= cookie_upper_bound) {
-    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
-               << " has " << observers_.size()
-               << " observers already (No Resource)";
-    return kObserversCookieUndefined;
-  }
-  std::shared_ptr<struct PortStatusCallbacks> cb =
-      std::make_shared<struct PortStatusCallbacks>();
-  *cb = cbacks;
-  observers_[cookie] = cb;
-  return cookie;
-}
-
-// The control function helps the bluetooth_audio module to unregister
-// PortStatusCallbacks
-// @param: cookie - indicates which bluetooth_audio output is
-void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (observers_.erase(cookie) != 1) {
-    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
-                 << " no such provider=0x"
-                 << android::base::StringPrintf("%04x", cookie);
-  }
-}
-
-// The control function is for the bluetooth_audio module to get the current
-// AudioConfiguration
-const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (IsSessionReady()) {
-    return audio_config_;
-  } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    return kInvalidOffloadAudioConfiguration;
-  } else {
-    return kInvalidSoftwareAudioConfiguration;
-  }
-}
-
-// Those control functions are for the bluetooth_audio module to start, suspend,
-// stop stream, to check position, and to update metadata.
-bool BluetoothAudioSession::StartStream() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (!IsSessionReady()) {
-    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
-               << " has NO session";
-    return false;
-  }
-  auto hal_retval = stack_iface_->startStream();
-  if (!hal_retval.isOk()) {
-    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
-                 << toString(session_type_) << " failed";
-    return false;
-  }
-  return true;
-}
-
-bool BluetoothAudioSession::SuspendStream() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (!IsSessionReady()) {
-    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
-               << " has NO session";
-    return false;
-  }
-  auto hal_retval = stack_iface_->suspendStream();
-  if (!hal_retval.isOk()) {
-    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
-                 << toString(session_type_) << " failed";
-    return false;
-  }
-  return true;
-}
-
-void BluetoothAudioSession::StopStream() {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (!IsSessionReady()) {
-    return;
-  }
-  auto hal_retval = stack_iface_->stopStream();
-  if (!hal_retval.isOk()) {
-    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
-                 << toString(session_type_) << " failed";
-  }
-}
-
-bool BluetoothAudioSession::GetPresentationPosition(
-    uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
-    timespec* data_position) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (!IsSessionReady()) {
-    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
-               << " has NO session";
-    return false;
-  }
-  bool retval = false;
-  auto hal_retval = stack_iface_->getPresentationPosition(
-      [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
-          BluetoothAudioStatus status,
-          const uint64_t& remoteDeviceAudioDelayNanos,
-          uint64_t transmittedOctets,
-          const TimeSpec& transmittedOctetsTimeStamp) {
-        if (status == BluetoothAudioStatus::SUCCESS) {
-          if (remote_delay_report_ns)
-            *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
-          if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
-          if (data_position)
-            *data_position =
-                timespec_convert_from_hal(transmittedOctetsTimeStamp);
-          retval = true;
-        }
-      });
-  if (!hal_retval.isOk()) {
-    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
-                 << toString(session_type_) << " failed";
-    return false;
-  }
-  return retval;
-}
-
-void BluetoothAudioSession::UpdateTracksMetadata(
-    const struct source_metadata* source_metadata) {
-  std::lock_guard<std::recursive_mutex> guard(mutex_);
-  if (!IsSessionReady()) {
-    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
-               << " has NO session";
-    return;
-  }
-
-  ssize_t track_count = source_metadata->track_count;
-  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
-            << track_count << " track(s)";
-  if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
-      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
-    return;
-  }
-
-  struct playback_track_metadata* track = source_metadata->tracks;
-  SourceMetadata sourceMetadata;
-  PlaybackTrackMetadata* halMetadata;
-
-  sourceMetadata.tracks.resize(track_count);
-  halMetadata = sourceMetadata.tracks.data();
-  while (track_count && track) {
-    halMetadata->usage = static_cast<AudioUsage>(track->usage);
-    halMetadata->contentType =
-        static_cast<AudioContentType>(track->content_type);
-    halMetadata->gain = track->gain;
-    LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
-                 << ", usage=" << toString(halMetadata->usage)
-                 << ", content=" << toString(halMetadata->contentType)
-                 << ", gain=" << halMetadata->gain;
-    --track_count;
-    ++track;
-    ++halMetadata;
-  }
-  auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
-  if (!hal_retval.isOk()) {
-    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
-                 << toString(session_type_) << " failed";
-  }
-}
-
-// The control function writes stream to FMQ
-size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
-                                              size_t bytes) {
-  if (buffer == nullptr || !bytes) return 0;
-  size_t totalWritten = 0;
-  int ms_timeout = kFmqSendTimeoutMs;
-  do {
-    std::unique_lock<std::recursive_mutex> lock(mutex_);
-    if (!IsSessionReady()) break;
-    size_t availableToWrite = mDataMQ->availableToWrite();
-    if (availableToWrite) {
-      if (availableToWrite > (bytes - totalWritten)) {
-        availableToWrite = bytes - totalWritten;
-      }
-
-      if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
-                          availableToWrite)) {
-        ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
-        return totalWritten;
-      }
-      totalWritten += availableToWrite;
-    } else if (ms_timeout >= kWritePollMs) {
-      lock.unlock();
-      usleep(kWritePollMs * 1000);
-      ms_timeout -= kWritePollMs;
-    } else {
-      ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
-            (kFmqSendTimeoutMs - ms_timeout));
-      return totalWritten;
-    }
-  } while (totalWritten < bytes);
-  return totalWritten;
-}
-
-std::unique_ptr<BluetoothAudioSessionInstance>
-    BluetoothAudioSessionInstance::instance_ptr =
-        std::unique_ptr<BluetoothAudioSessionInstance>(
-            new BluetoothAudioSessionInstance());
-
-// API to fetch the session of A2DP / Hearing Aid
-std::shared_ptr<BluetoothAudioSession>
-BluetoothAudioSessionInstance::GetSessionInstance(
-    const SessionType& session_type) {
-  std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
-  if (!instance_ptr->sessions_map_.empty()) {
-    auto entry = instance_ptr->sessions_map_.find(session_type);
-    if (entry != instance_ptr->sessions_map_.end()) {
-      return entry->second;
-    }
-  }
-  std::shared_ptr<BluetoothAudioSession> session_ptr =
-      std::make_shared<BluetoothAudioSession>(session_type);
-  instance_ptr->sessions_map_[session_type] = session_ptr;
-  return session_ptr;
-}
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h b/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
deleted file mode 100644
index 838d1cc..0000000
--- a/bluetooth/audio/2.0/default/session/BluetoothAudioSession.h
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * Copyright 2018 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.
- */
-
-#pragma once
-
-#include <mutex>
-#include <unordered_map>
-
-#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
-#include <fmq/MessageQueue.h>
-#include <hardware/audio.h>
-#include <hidl/MQDescriptor.h>
-
-namespace android {
-namespace bluetooth {
-namespace audio {
-
-using ::android::sp;
-using ::android::hardware::kSynchronizedReadWrite;
-using ::android::hardware::MessageQueue;
-using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
-using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
-using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
-using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
-using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
-using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
-using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
-using ::android::hardware::bluetooth::audio::V2_0::SessionType;
-
-using BluetoothAudioStatus =
-    ::android::hardware::bluetooth::audio::V2_0::Status;
-
-using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
-
-static constexpr uint16_t kObserversCookieSize = 0x0010;  // 0x0000 ~ 0x000f
-constexpr uint16_t kObserversCookieUndefined =
-    (static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
-inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
-  return static_cast<SessionType>(cookie >> 8 & 0x00ff);
-}
-inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
-  return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
-}
-inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
-  return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
-         kObserversCookieSize;
-}
-
-// This presents the callbacks of started / suspended and session changed,
-// and the bluetooth_audio module uses to receive the status notification
-struct PortStatusCallbacks {
-  // control_result_cb_ - when the Bluetooth stack reports results of
-  // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
-  // this callback to report to the bluetooth_audio module.
-  // @param: cookie - indicates which bluetooth_audio output should handle
-  // @param: start_resp - this report is for startStream or not
-  // @param: status - the result of startStream
-  std::function<void(uint16_t cookie, bool start_resp,
-                     const BluetoothAudioStatus& status)>
-      control_result_cb_;
-  // session_changed_cb_ - when the Bluetooth stack start / end session, the
-  // BluetoothAudioProvider will invoke this callback to notify to the
-  // bluetooth_audio module.
-  // @param: cookie - indicates which bluetooth_audio output should handle
-  std::function<void(uint16_t cookie)> session_changed_cb_;
-};
-
-class BluetoothAudioSession {
- private:
-  // using recursive_mutex to allow hwbinder to re-enter agian.
-  std::recursive_mutex mutex_;
-  SessionType session_type_;
-
-  // audio control path to use for both software and offloading
-  sp<IBluetoothAudioPort> stack_iface_;
-  // audio data path (FMQ) for software encoding
-  std::unique_ptr<DataMQ> mDataMQ;
-  // audio data configuration for both software and offloading
-  AudioConfiguration audio_config_;
-
-  static AudioConfiguration invalidSoftwareAudioConfiguration;
-  static AudioConfiguration invalidOffloadAudioConfiguration;
-
-  // saving those registered bluetooth_audio's callbacks
-  std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
-      observers_;
-
-  bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
-  bool UpdateAudioConfig(const AudioConfiguration& audio_config);
-  // invoking the registered session_changed_cb_
-  void ReportSessionStatus();
-
- public:
-  BluetoothAudioSession(const SessionType& session_type);
-
-  // The function helps to check if this session is ready or not
-  // @return: true if the Bluetooth stack has started the specified session
-  bool IsSessionReady();
-
-  // The report function is used to report that the Bluetooth stack has started
-  // this session without any failure, and will invoke session_changed_cb_ to
-  // notify those registered bluetooth_audio outputs
-  void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
-                        const DataMQ::Descriptor* dataMQ,
-                        const AudioConfiguration& audio_config);
-
-  // The report function is used to report that the Bluetooth stack has ended
-  // the session, and will invoke session_changed_cb_ to notify registered
-  // bluetooth_audio outputs
-  void OnSessionEnded();
-
-  // The report function is used to report that the Bluetooth stack has notified
-  // the result of startStream or suspendStream, and will invoke
-  // control_result_cb_ to notify registered bluetooth_audio outputs
-  void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
-
-  // The control function helps the bluetooth_audio module to register
-  // PortStatusCallbacks
-  // @return: cookie - the assigned number to this bluetooth_audio output
-  uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
-
-  // The control function helps the bluetooth_audio module to unregister
-  // PortStatusCallbacks
-  // @param: cookie - indicates which bluetooth_audio output is
-  void UnregisterStatusCback(uint16_t cookie);
-
-  // The control function is for the bluetooth_audio module to get the current
-  // AudioConfiguration
-  const AudioConfiguration& GetAudioConfig();
-
-  // Those control functions are for the bluetooth_audio module to start,
-  // suspend, stop stream, to check position, and to update metadata.
-  bool StartStream();
-  bool SuspendStream();
-  void StopStream();
-  bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
-                               uint64_t* total_bytes_readed,
-                               timespec* data_position);
-  void UpdateTracksMetadata(const struct source_metadata* source_metadata);
-
-  // The control function writes stream to FMQ
-  size_t OutWritePcmData(const void* buffer, size_t bytes);
-
-  static constexpr PcmParameters kInvalidPcmParameters = {
-      .sampleRate = SampleRate::RATE_UNKNOWN,
-      .channelMode = ChannelMode::UNKNOWN,
-      .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
-  };
-  // can't be constexpr because of non-literal type
-  static const CodecConfiguration kInvalidCodecConfiguration;
-
-  static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
-      invalidSoftwareAudioConfiguration;
-  static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
-      invalidOffloadAudioConfiguration;
-};
-
-class BluetoothAudioSessionInstance {
- public:
-  // The API is to fetch the specified session of A2DP / Hearing Aid
-  static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
-      const SessionType& session_type);
-
- private:
-  static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
-  std::mutex mutex_;
-  std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
-      sessions_map_;
-};
-
-}  // namespace audio
-}  // namespace bluetooth
-}  // namespace android
diff --git a/bluetooth/audio/2.0/vts/functional/Android.bp b/bluetooth/audio/2.0/vts/functional/Android.bp
index 0ed5da4..f5cb956 100644
--- a/bluetooth/audio/2.0/vts/functional/Android.bp
+++ b/bluetooth/audio/2.0/vts/functional/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBluetoothAudioV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
index b3cb6f7..ac97b52 100644
--- a/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
+++ b/bluetooth/audio/2.0/vts/functional/VtsHalBluetoothAudioV2_0TargetTest.cpp
@@ -888,20 +888,28 @@
     android::hardware::getAllHalInstanceNames(
         IBluetoothAudioProvidersFactory::descriptor);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProvidersFactoryHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProvidersFactoryHidlTest,
                          testing::ValuesIn(kAudioInstances),
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderA2dpSoftwareHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance,
                          BluetoothAudioProviderA2dpSoftwareHidlTest,
                          testing::ValuesIn(kAudioInstances),
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderA2dpHardwareHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance,
                          BluetoothAudioProviderA2dpHardwareHidlTest,
                          testing::ValuesIn(kAudioInstances),
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderHearingAidSoftwareHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance,
                          BluetoothAudioProviderHearingAidSoftwareHidlTest,
                          testing::ValuesIn(kAudioInstances),
diff --git a/bluetooth/audio/2.1/Android.bp b/bluetooth/audio/2.1/Android.bp
new file mode 100644
index 0000000..822f5b3
--- /dev/null
+++ b/bluetooth/audio/2.1/Android.bp
@@ -0,0 +1,31 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.bluetooth.audio@2.1",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IBluetoothAudioProvider.hal",
+        "IBluetoothAudioProvidersFactory.hal",
+    ],
+    interfaces: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.bluetooth.updatable",
+    ],
+    gen_java: false,
+}
diff --git a/bluetooth/audio/2.1/IBluetoothAudioProvider.hal b/bluetooth/audio/2.1/IBluetoothAudioProvider.hal
new file mode 100644
index 0000000..c462b9e
--- /dev/null
+++ b/bluetooth/audio/2.1/IBluetoothAudioProvider.hal
@@ -0,0 +1,62 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import @2.0::IBluetoothAudioProvider;
+import @2.0::IBluetoothAudioPort;
+import @2.0::Status;
+
+/**
+ * HAL interface from the Bluetooth stack to the Audio HAL
+ *
+ * The Bluetooth stack calls methods in this interface to start and end audio
+ * sessions and sends callback events to the Audio HAL.
+ *
+ * Note: For HIDL APIs with a "generates" statement, the callback parameter used
+ * for return value must be invoked synchronously before the API call returns.
+ */
+interface IBluetoothAudioProvider extends @2.0::IBluetoothAudioProvider {
+
+    /**
+     * This method indicates that the Bluetooth stack is ready to stream audio.
+     * It registers an instance of IBluetoothAudioPort with and provides the
+     * current negotiated codec to the Audio HAL. After this method is called,
+     * the Audio HAL can invoke IBluetoothAudioPort.startStream().
+     *
+     * Note: endSession() must be called to unregister this IBluetoothAudioPort
+     *
+     * @param hostIf An instance of IBluetoothAudioPort for stream control
+     * @param audioConfig The audio configuration negotiated with the remote
+     *    device. The PCM parameters are set if software based encoding,
+     *    otherwise the correct codec configuration is used for hardware
+     *    encoding.
+     *
+     * @return status One of the following
+     *    SUCCESS if this IBluetoothAudioPort was successfully registered with
+     *        the Audio HAL
+     *    UNSUPPORTED_CODEC_CONFIGURATION if the Audio HAL cannot register this
+     *        IBluetoothAudioPort with the given codec configuration
+     *    FAILURE if the Audio HAL cannot register this IBluetoothAudioPort for
+     *        any other reason
+     * @return dataMQ The fast message queue for audio data from/to this
+     *    provider. Audio data will be in PCM format as specified by the
+     *    audioConfig.pcmConfig parameter. Invalid if streaming is offloaded
+     *    from/to hardware or on failure.
+     */
+    startSession_2_1(IBluetoothAudioPort hostIf, AudioConfiguration audioConfig)
+                generates (Status status, fmq_sync<uint8_t> dataMQ);
+};
diff --git a/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal b/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal
new file mode 100644
index 0000000..5593c7c
--- /dev/null
+++ b/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.hal
@@ -0,0 +1,73 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import IBluetoothAudioProvider;
+import @2.0::IBluetoothAudioProvidersFactory;
+import @2.0::Status;
+
+/**
+ * This factory allows a HAL implementation to be split into multiple
+ * independent providers.
+ *
+ * When the Bluetooth stack is ready to create an audio session, it must first
+ * obtain the IBluetoothAudioProvider for that session type by calling
+ * openProvider().
+ *
+ * Note: For HIDL APIs with a "generates" statement, the callback parameter used
+ * for return value must be invoked synchronously before the API call returns.
+ */
+interface IBluetoothAudioProvidersFactory extends @2.0::IBluetoothAudioProvidersFactory {
+
+    /**
+     * Opens an audio provider for a session type. To close the provider, it is
+     * necessary to release references to the returned provider object.
+     *
+     * @param sessionType The session type (e.g.
+     *    LE_AUDIO_SOFTWARE_ENCODING_DATAPATH).
+     *
+     * @return status One of the following
+     *    SUCCESS if the Audio HAL successfully opens the provider with the
+     *        given session type
+     *    FAILURE if the Audio HAL cannot open the provider
+     * @return provider The provider of the specified session type
+     */
+    openProvider_2_1(SessionType sessionType)
+        generates (Status status, IBluetoothAudioProvider provider);
+
+    /**
+     * Gets a list of audio capabilities for a session type.
+     *
+     * For software encoding, the PCM capabilities are returned.
+     * For hardware encoding, the supported codecs and their capabilities are
+     * returned.
+     *
+     * @param sessionType The session type (e.g.
+     *    A2DP_SOFTWARE_ENCODING_DATAPATH).
+     * @return audioCapabilities A list containing all the capabilities
+     *    supported by the sesson type. The capabilities is a list of
+     *    available options when configuring the codec for the session.
+     *    For software encoding it is the PCM data rate.
+     *    For hardware encoding it is the list of supported codecs and their
+     *    capabilities.
+     *    If a provider isn't supported, an empty list should be returned.
+     *    Note: Only one entry should exist per codec when using hardware
+     *    encoding.
+     */
+     getProviderCapabilities_2_1(SessionType sessionType)
+         generates (vec<AudioCapabilities> audioCapabilities);
+};
diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
new file mode 100644
index 0000000..3fe1a4d
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.cpp
@@ -0,0 +1,95 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpOffload"
+
+#include "A2dpOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+A2dpOffloadAudioProvider::A2dpOffloadAudioProvider()
+    : BluetoothAudioProvider() {
+  session_type_ = SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH;
+}
+
+bool A2dpOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  return isValid(static_cast<SessionType>(sessionType));
+}
+
+bool A2dpOffloadAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_);
+}
+
+Return<void> A2dpOffloadAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::codecConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsOffloadCodecConfigurationValid(
+                 session_type_, audioConfig.codecConfig())) {
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpOffloadAudioProvider::onSessionReady(
+    startSession_cb _hidl_cb) {
+  BluetoothAudioSessionReport_2_1::OnSessionStarted(session_type_, stack_iface_,
+                                                    nullptr, audio_config_);
+  _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h
new file mode 100644
index 0000000..13e0b9a
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpOffloadAudioProvider.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class A2dpOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+  A2dpOffloadAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+                            const V2_0::AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
new file mode 100644
index 0000000..a37176b
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.cpp
@@ -0,0 +1,113 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderA2dpSoftware"
+
+#include "A2dpSoftwareAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+static constexpr uint32_t kPcmFrameSize = 4;  // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7;  // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 2;  // double buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+A2dpSoftwareAudioProvider::A2dpSoftwareAudioProvider()
+    : BluetoothAudioProvider(), mDataMQ(nullptr) {
+  LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+            << " byte(s)";
+  std::unique_ptr<DataMQ> tempDataMQ(
+      new DataMQ(kDataMqSize, /* EventFlag */ true));
+  if (tempDataMQ && tempDataMQ->isValid()) {
+    mDataMQ = std::move(tempDataMQ);
+    session_type_ = SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH;
+  } else {
+    ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+    ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+  }
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  return isValid(static_cast<SessionType>(sessionType));
+}
+
+bool A2dpSoftwareAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> A2dpSoftwareAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::pcmConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+                 audioConfig.pcmConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+                 << toString(audioConfig.pcmConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> A2dpSoftwareAudioProvider::onSessionReady(
+    startSession_cb _hidl_cb) {
+  if (mDataMQ && mDataMQ->isValid()) {
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
+        session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+    _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+  } else {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+  }
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h
new file mode 100644
index 0000000..20566d1
--- /dev/null
+++ b/bluetooth/audio/2.1/default/A2dpSoftwareAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class A2dpSoftwareAudioProvider : public BluetoothAudioProvider {
+ public:
+  A2dpSoftwareAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const V2_0::AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  // audio data queue for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/Android.bp b/bluetooth/audio/2.1/default/Android.bp
new file mode 100644
index 0000000..3000223
--- /dev/null
+++ b/bluetooth/audio/2.1/default/Android.bp
@@ -0,0 +1,36 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+    name: "android.hardware.bluetooth.audio@2.1-impl",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    relative_install_path: "hw",
+    srcs: [
+        "BluetoothAudioProvidersFactory.cpp",
+        "BluetoothAudioProvider.cpp",
+        "A2dpOffloadAudioProvider.cpp",
+        "A2dpSoftwareAudioProvider.cpp",
+        "HearingAidAudioProvider.cpp",
+        "LeAudioAudioProvider.cpp",
+        "LeAudioOffloadAudioProvider.cpp",
+    ],
+    header_libs: ["libhardware_headers"],
+    shared_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "libbase",
+        "libbluetooth_audio_session",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
new file mode 100644
index 0000000..38889ae
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.cpp
@@ -0,0 +1,164 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderStub"
+
+#include "BluetoothAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Void;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+void BluetoothAudioDeathRecipient::serviceDied(
+    uint64_t cookie __unused,
+    const wp<::android::hidl::base::V1_0::IBase>& who __unused) {
+  LOG(ERROR) << "BluetoothAudioDeathRecipient::" << __func__
+             << " - BluetoothAudio Service died";
+  provider_->endSession();
+}
+
+BluetoothAudioProvider::BluetoothAudioProvider()
+    : death_recipient_(new BluetoothAudioDeathRecipient(this)),
+      session_type_(SessionType::UNKNOWN),
+      audio_config_({}) {}
+
+Return<void> BluetoothAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  AudioConfiguration audioConfig_2_1;
+
+  if (audioConfig.getDiscriminator() ==
+      V2_0::AudioConfiguration::hidl_discriminator::pcmConfig) {
+    audioConfig_2_1.pcmConfig({
+        .sampleRate =
+            static_cast<SampleRate>(audioConfig.pcmConfig().sampleRate),
+        .channelMode = audioConfig.pcmConfig().channelMode,
+        .bitsPerSample = audioConfig.pcmConfig().bitsPerSample,
+        .dataIntervalUs = 0});
+  } else {
+    audioConfig_2_1.codecConfig(audioConfig.codecConfig());
+  }
+
+  return startSession_2_1(hostIf, audioConfig_2_1, _hidl_cb);
+}
+
+Return<void> BluetoothAudioProvider::startSession_2_1(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  if (hostIf == nullptr) {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+    return Void();
+  }
+
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  audio_config_ = audioConfig;
+  stack_iface_ = hostIf;
+  stack_iface_->linkToDeath(death_recipient_, 0);
+
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", AudioConfiguration=[" << toString(audio_config_) << "]";
+
+  onSessionReady(_hidl_cb);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamStarted(
+    BluetoothAudioStatus status) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", status=" << toString(status);
+
+  /**
+   * Streaming on control path has started,
+   * HAL server should start the streaming on data path.
+   */
+  if (stack_iface_) {
+    BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, true,
+                                                         status);
+  } else {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", status=" << toString(status) << " has NO session";
+  }
+
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::streamSuspended(
+    BluetoothAudioStatus status) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+            << ", status=" << toString(status);
+
+  /**
+   * Streaming on control path has suspend,
+   * HAL server should suspend the streaming on data path.
+   */
+  if (stack_iface_) {
+    BluetoothAudioSessionReport_2_1::ReportControlStatus(session_type_, false,
+                                                         status);
+  } else {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", status=" << toString(status) << " has NO session";
+  }
+
+  return Void();
+}
+
+Return<void> BluetoothAudioProvider::endSession() {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+
+  if (stack_iface_) {
+    BluetoothAudioSessionReport_2_1::OnSessionEnded(session_type_);
+    stack_iface_->unlinkToDeath(death_recipient_);
+  } else {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " has NO session";
+  }
+
+  /**
+   * Clean up the audio platform as remote audio device is no
+   * longer active
+   */
+  stack_iface_ = nullptr;
+  audio_config_ = {};
+
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvider.h b/bluetooth/audio/2.1/default/BluetoothAudioProvider.h
new file mode 100644
index 0000000..a2e611f
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvider.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::sp;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+
+class BluetoothAudioDeathRecipient;
+
+class BluetoothAudioProvider : public IBluetoothAudioProvider {
+ public:
+  BluetoothAudioProvider();
+  ~BluetoothAudioProvider() = default;
+
+  virtual bool isValid(const SessionType& sessionType) = 0;
+  virtual bool isValid(const V2_0::SessionType& sessionType) = 0;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const V2_0::AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+  Return<void> startSession_2_1(const sp<IBluetoothAudioPort>& hostIf,
+                                const AudioConfiguration& audioConfig,
+                                startSession_cb _hidl_cb) override;
+  Return<void> streamStarted(BluetoothAudioStatus status) override;
+  Return<void> streamSuspended(BluetoothAudioStatus status) override;
+  Return<void> endSession() override;
+
+ protected:
+  sp<BluetoothAudioDeathRecipient> death_recipient_;
+
+  SessionType session_type_;
+  AudioConfiguration audio_config_;
+  sp<V2_0::IBluetoothAudioPort> stack_iface_;
+
+  virtual Return<void> onSessionReady(startSession_cb _hidl_cb) = 0;
+};
+
+class BluetoothAudioDeathRecipient : public hidl_death_recipient {
+ public:
+  BluetoothAudioDeathRecipient(const sp<BluetoothAudioProvider> provider)
+      : provider_(provider) {}
+
+  virtual void serviceDied(
+      uint64_t cookie,
+      const wp<::android::hidl::base::V1_0::IBase>& who) override;
+
+ private:
+  sp<BluetoothAudioProvider> provider_;
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
new file mode 100644
index 0000000..b0d171a
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.cpp
@@ -0,0 +1,184 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProvidersFactory"
+
+#include "BluetoothAudioProvidersFactory.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+
+A2dpSoftwareAudioProvider
+    BluetoothAudioProvidersFactory::a2dp_software_provider_instance_;
+A2dpOffloadAudioProvider
+    BluetoothAudioProvidersFactory::a2dp_offload_provider_instance_;
+HearingAidAudioProvider
+    BluetoothAudioProvidersFactory::hearing_aid_provider_instance_;
+LeAudioOutputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_output_provider_instance_;
+LeAudioOffloadOutputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_offload_output_provider_instance_;
+LeAudioInputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_input_provider_instance_;
+LeAudioOffloadInputAudioProvider
+    BluetoothAudioProvidersFactory::leaudio_offload_input_provider_instance_;
+
+Return<void> BluetoothAudioProvidersFactory::openProvider(
+    const V2_0::SessionType sessionType, openProvider_cb _hidl_cb) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+  BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+  BluetoothAudioProvider* provider = nullptr;
+  switch (sessionType) {
+    case V2_0::SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+      provider = &a2dp_software_provider_instance_;
+      break;
+    case V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+      provider = &a2dp_offload_provider_instance_;
+      break;
+    case V2_0::SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+      provider = &hearing_aid_provider_instance_;
+      break;
+    default:
+      status = BluetoothAudioStatus::FAILURE;
+  }
+  if (provider == nullptr || !provider->isValid(sessionType)) {
+    provider = nullptr;
+    status = BluetoothAudioStatus::FAILURE;
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+               << ", status=" << toString(status);
+  }
+  _hidl_cb(status, provider);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::openProvider_2_1(
+    const SessionType sessionType, openProvider_2_1_cb _hidl_cb) {
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType);
+  BluetoothAudioStatus status = BluetoothAudioStatus::SUCCESS;
+  BluetoothAudioProvider* provider = nullptr;
+  switch (sessionType) {
+    case SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH:
+      provider = &a2dp_software_provider_instance_;
+      break;
+    case SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH:
+      provider = &a2dp_offload_provider_instance_;
+      break;
+    case SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH:
+      provider = &hearing_aid_provider_instance_;
+      break;
+    case SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH:
+      provider = &leaudio_output_provider_instance_;
+      break;
+    case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH:
+      provider = &leaudio_offload_output_provider_instance_;
+      break;
+    case SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH:
+      provider = &leaudio_input_provider_instance_;
+      break;
+    case SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH:
+      provider = &leaudio_offload_input_provider_instance_;
+      break;
+    default:
+      status = BluetoothAudioStatus::FAILURE;
+  }
+  if (provider == nullptr || !provider->isValid(sessionType)) {
+    provider = nullptr;
+    status = BluetoothAudioStatus::FAILURE;
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(sessionType)
+               << ", status=" << toString(status);
+  }
+  _hidl_cb(status, provider);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities(
+    const V2_0::SessionType sessionType, getProviderCapabilities_cb _hidl_cb) {
+  hidl_vec<V2_0::AudioCapabilities> audio_capabilities =
+      hidl_vec<V2_0::AudioCapabilities>(0);
+  if (sessionType == V2_0::SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    std::vector<CodecCapabilities> db_codec_capabilities =
+        android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+    if (db_codec_capabilities.size()) {
+      audio_capabilities.resize(db_codec_capabilities.size());
+      for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+        audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+      }
+    }
+  } else if (sessionType != V2_0::SessionType::UNKNOWN) {
+    std::vector<::android::hardware::bluetooth::audio::V2_0::PcmParameters>
+        db_pcm_capabilities =
+            android::bluetooth::audio::GetSoftwarePcmCapabilities();
+    if (db_pcm_capabilities.size() == 1) {
+      audio_capabilities.resize(1);
+      audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+    }
+  }
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+            << " supports " << audio_capabilities.size() << " codecs";
+  _hidl_cb(audio_capabilities);
+  return Void();
+}
+
+Return<void> BluetoothAudioProvidersFactory::getProviderCapabilities_2_1(
+    const SessionType sessionType, getProviderCapabilities_2_1_cb _hidl_cb) {
+  hidl_vec<AudioCapabilities> audio_capabilities =
+      hidl_vec<AudioCapabilities>(0);
+  if (sessionType == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    std::vector<CodecCapabilities> db_codec_capabilities =
+        android::bluetooth::audio::GetOffloadCodecCapabilities(sessionType);
+    if (db_codec_capabilities.size()) {
+      audio_capabilities.resize(db_codec_capabilities.size());
+      for (int i = 0; i < db_codec_capabilities.size(); ++i) {
+        audio_capabilities[i].codecCapabilities(db_codec_capabilities[i]);
+      }
+    }
+  } else if (sessionType != SessionType::UNKNOWN) {
+    std::vector<PcmParameters> db_pcm_capabilities =
+        android::bluetooth::audio::GetSoftwarePcmCapabilities_2_1();
+    if (db_pcm_capabilities.size() == 1) {
+      audio_capabilities.resize(1);
+      audio_capabilities[0].pcmCapabilities(db_pcm_capabilities[0]);
+    }
+  }
+  LOG(INFO) << __func__ << " - SessionType=" << toString(sessionType)
+            << " supports " << audio_capabilities.size() << " codecs";
+  _hidl_cb(audio_capabilities);
+  return Void();
+}
+
+IBluetoothAudioProvidersFactory* HIDL_FETCH_IBluetoothAudioProvidersFactory(
+    const char* /* name */) {
+  return new BluetoothAudioProvidersFactory();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
new file mode 100644
index 0000000..f8f557e
--- /dev/null
+++ b/bluetooth/audio/2.1/default/BluetoothAudioProvidersFactory.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
+
+#include "A2dpOffloadAudioProvider.h"
+#include "A2dpSoftwareAudioProvider.h"
+#include "BluetoothAudioProvider.h"
+#include "HearingAidAudioProvider.h"
+#include "LeAudioAudioProvider.h"
+#include "LeAudioOffloadAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class BluetoothAudioProvidersFactory : public IBluetoothAudioProvidersFactory {
+ public:
+  BluetoothAudioProvidersFactory() {}
+
+  Return<void> openProvider(const V2_0::SessionType sessionType,
+                            openProvider_cb _hidl_cb) override;
+
+  Return<void> getProviderCapabilities(
+      const V2_0::SessionType sessionType,
+      getProviderCapabilities_cb _hidl_cb) override;
+
+  Return<void> openProvider_2_1(const SessionType sessionType,
+                                openProvider_2_1_cb _hidl_cb) override;
+
+  Return<void> getProviderCapabilities_2_1(
+      const SessionType sessionType,
+      getProviderCapabilities_2_1_cb _hidl_cb) override;
+
+ private:
+  static A2dpSoftwareAudioProvider a2dp_software_provider_instance_;
+  static A2dpOffloadAudioProvider a2dp_offload_provider_instance_;
+  static HearingAidAudioProvider hearing_aid_provider_instance_;
+  static LeAudioOutputAudioProvider leaudio_output_provider_instance_;
+  static LeAudioInputAudioProvider leaudio_input_provider_instance_;
+  static LeAudioOffloadOutputAudioProvider leaudio_offload_output_provider_instance_;
+  static LeAudioOffloadInputAudioProvider leaudio_offload_input_provider_instance_;
+};
+
+extern "C" IBluetoothAudioProvidersFactory*
+HIDL_FETCH_IBluetoothAudioProvidersFactory(const char* name);
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
new file mode 100644
index 0000000..712bd4f
--- /dev/null
+++ b/bluetooth/audio/2.1/default/HearingAidAudioProvider.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderHearingAid"
+
+#include "HearingAidAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+
+static constexpr uint32_t kPcmFrameSize = 4;  // 16 bits per sample / stereo
+static constexpr uint32_t kPcmFrameCount = 128;
+static constexpr uint32_t kRtpFrameSize = kPcmFrameSize * kPcmFrameCount;
+static constexpr uint32_t kRtpFrameCount = 7;  // max counts by 1 tick (20ms)
+static constexpr uint32_t kBufferSize = kRtpFrameSize * kRtpFrameCount;
+static constexpr uint32_t kBufferCount = 1;  // single buffer
+static constexpr uint32_t kDataMqSize = kBufferSize * kBufferCount;
+
+HearingAidAudioProvider::HearingAidAudioProvider()
+    : BluetoothAudioProvider(), mDataMQ(nullptr) {
+  LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+            << " byte(s)";
+  std::unique_ptr<DataMQ> tempDataMQ(
+      new DataMQ(kDataMqSize, /* EventFlag */ true));
+  if (tempDataMQ && tempDataMQ->isValid()) {
+    mDataMQ = std::move(tempDataMQ);
+    session_type_ = SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH;
+  } else {
+    ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+    ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+  }
+}
+
+bool HearingAidAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  return isValid(static_cast<SessionType>(sessionType));
+}
+
+bool HearingAidAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_ && mDataMQ && mDataMQ->isValid());
+}
+
+Return<void> HearingAidAudioProvider::startSession(
+    const sp<IBluetoothAudioPort>& hostIf,
+    const V2_0::AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::pcmConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid(
+                 audioConfig.pcmConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+                 << toString(audioConfig.pcmConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession(hostIf, audioConfig, _hidl_cb);
+}
+
+Return<void> HearingAidAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+  if (mDataMQ && mDataMQ->isValid()) {
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
+        session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+    _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+  } else {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+  }
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/HearingAidAudioProvider.h b/bluetooth/audio/2.1/default/HearingAidAudioProvider.h
new file mode 100644
index 0000000..c949257
--- /dev/null
+++ b/bluetooth/audio/2.1/default/HearingAidAudioProvider.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class HearingAidAudioProvider : public BluetoothAudioProvider {
+ public:
+  HearingAidAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession(const sp<IBluetoothAudioPort>& hostIf,
+                            const V2_0::AudioConfiguration& audioConfig,
+                            startSession_cb _hidl_cb) override;
+
+ private:
+  // audio data queue for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
new file mode 100644
index 0000000..2ebf6c5
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioAudioProvider.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
+ * www.ehima.com
+ *
+ * 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.
+ */
+
+#define LOG_TAG "BTAudioProviderLeAudio"
+
+#include "LeAudioAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+static constexpr uint32_t kBufferOutCount = 2;  // two frame buffer
+static constexpr uint32_t kBufferInCount = 2;   // two frame buffer
+
+LeAudioOutputAudioProvider::LeAudioOutputAudioProvider()
+    : LeAudioAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH;
+}
+
+LeAudioInputAudioProvider::LeAudioInputAudioProvider()
+    : LeAudioAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH;
+}
+
+LeAudioAudioProvider::LeAudioAudioProvider()
+    : BluetoothAudioProvider(), mDataMQ(nullptr) {}
+
+bool LeAudioAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  LOG(ERROR) << __func__ << ", invalid session type for Le Audio provider: "
+             << toString(sessionType);
+
+  return false;
+}
+
+bool LeAudioAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_);
+}
+
+Return<void> LeAudioAudioProvider::startSession_2_1(
+    const sp<V2_0::IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::pcmConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  } else if (!android::bluetooth::audio::IsSoftwarePcmConfigurationValid_2_1(
+                 audioConfig.pcmConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported PCM Configuration="
+                 << toString(audioConfig.pcmConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  uint32_t kDataMqSize = 0;
+  switch (audioConfig.pcmConfig().sampleRate) {
+    case SampleRate::RATE_8000:
+      kDataMqSize = 8000;
+      break;
+    case SampleRate::RATE_16000:
+      kDataMqSize = 16000;
+      break;
+    case SampleRate::RATE_24000:
+      kDataMqSize = 24000;
+      break;
+    case SampleRate::RATE_32000:
+      kDataMqSize = 32000;
+      break;
+    case SampleRate::RATE_44100:
+      kDataMqSize = 44100;
+      break;
+    case SampleRate::RATE_48000:
+      kDataMqSize = 48000;
+      break;
+    default:
+      LOG(WARNING) << __func__ << " - Unsupported sampling frequency="
+                   << toString(audioConfig.pcmConfig());
+      _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+               DataMQ::Descriptor());
+      return Void();
+  }
+
+  /* Number of samples per millisecond */
+  kDataMqSize = ceil(kDataMqSize / 1000);
+
+  switch (audioConfig.pcmConfig().channelMode) {
+    case ChannelMode::MONO:
+      break;
+    case ChannelMode::STEREO:
+      kDataMqSize *= 2;
+      break;
+    default:
+      /* This should never happen it would be caught while validating
+       * parameters.
+       */
+      break;
+  }
+
+  switch (audioConfig.pcmConfig().bitsPerSample) {
+    case BitsPerSample::BITS_16:
+      kDataMqSize *= 2;
+      break;
+    case BitsPerSample::BITS_24:
+      kDataMqSize *= 3;
+      break;
+    case BitsPerSample::BITS_32:
+      kDataMqSize *= 4;
+      break;
+    default:
+      /* This should never happen it would be caught while validating
+       * parameters.
+       */
+      break;
+  }
+
+  if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH)
+    kDataMqSize *= kBufferOutCount;
+  else if (session_type_ == SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH)
+    kDataMqSize *= kBufferInCount;
+  else
+    LOG(WARNING) << __func__ << ", default single buffer used";
+
+  kDataMqSize *= audioConfig.pcmConfig().dataIntervalUs / 1000;
+
+  LOG(INFO) << __func__ << " - size of audio buffer " << kDataMqSize
+            << " byte(s)";
+
+  std::unique_ptr<DataMQ> tempDataMQ(
+      new DataMQ(kDataMqSize, /* EventFlag */ true));
+  if (tempDataMQ && tempDataMQ->isValid()) {
+    mDataMQ = std::move(tempDataMQ);
+  } else {
+    ALOGE_IF(!tempDataMQ, "failed to allocate data MQ");
+    ALOGE_IF(tempDataMQ && !tempDataMQ->isValid(), "data MQ is invalid");
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession_2_1(hostIf, audioConfig,
+                                                  _hidl_cb);
+}
+
+Return<void> LeAudioAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+  if (mDataMQ && mDataMQ->isValid()) {
+    BluetoothAudioSessionReport_2_1::OnSessionStarted(
+        session_type_, stack_iface_, mDataMQ->getDesc(), audio_config_);
+    _hidl_cb(BluetoothAudioStatus::SUCCESS, *mDataMQ->getDesc());
+  } else {
+    _hidl_cb(BluetoothAudioStatus::FAILURE, DataMQ::Descriptor());
+  }
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioAudioProvider.h b/bluetooth/audio/2.1/default/LeAudioAudioProvider.h
new file mode 100644
index 0000000..09b2b54
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioAudioProvider.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2020 HIMSA II K/S - www.himsa.com. Represented by EHIMA -
+ * www.ehima.com
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+class LeAudioAudioProvider : public BluetoothAudioProvider {
+ public:
+  LeAudioAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+                                const AudioConfiguration& audioConfig,
+                                startSession_cb _hidl_cb) override;
+
+ private:
+  /** queue for software encodec/decoded audio data */
+  std::unique_ptr<DataMQ> mDataMQ;
+
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOutputAudioProvider : public LeAudioAudioProvider {
+ public:
+  LeAudioOutputAudioProvider();
+};
+
+class LeAudioInputAudioProvider : public LeAudioAudioProvider {
+ public:
+  LeAudioInputAudioProvider();
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp
new file mode 100644
index 0000000..c11bdad
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "BTAudioProviderLeAudioOffload"
+
+#include "LeAudioOffloadAudioProvider.h"
+
+#include <android-base/logging.h>
+
+#include "BluetoothAudioSessionReport_2_1.h"
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+using ::android::bluetooth::audio::BluetoothAudioSessionReport_2_1;
+using ::android::hardware::Void;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+LeAudioOffloadOutputAudioProvider::LeAudioOffloadOutputAudioProvider()
+    : LeAudioOffloadAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH;
+}
+
+LeAudioOffloadInputAudioProvider::LeAudioOffloadInputAudioProvider()
+    : LeAudioOffloadAudioProvider() {
+  session_type_ = SessionType::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH;
+}
+
+LeAudioOffloadAudioProvider::LeAudioOffloadAudioProvider()
+    : BluetoothAudioProvider() {}
+
+bool LeAudioOffloadAudioProvider::isValid(const V2_0::SessionType& sessionType) {
+  LOG(ERROR) << __func__ << ", invalid session type for Offloaded Le Audio provider: "
+             << toString(sessionType);
+
+  return false;
+}
+
+bool LeAudioOffloadAudioProvider::isValid(const SessionType& sessionType) {
+  return (sessionType == session_type_);
+}
+
+Return<void> LeAudioOffloadAudioProvider::startSession_2_1(
+    const sp<V2_0::IBluetoothAudioPort>& hostIf,
+    const AudioConfiguration& audioConfig, startSession_cb _hidl_cb) {
+  /**
+   * Initialize the audio platform if audioConfiguration is supported.
+   * Save the IBluetoothAudioPort interface, so that it can be used
+   * later to send stream control commands to the HAL client, based on
+   * interaction with Audio framework.
+   */
+  if (audioConfig.getDiscriminator() !=
+      AudioConfiguration::hidl_discriminator::leAudioCodecConfig) {
+    LOG(WARNING) << __func__
+                 << " - Invalid Audio Configuration=" << toString(audioConfig);
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  if (!android::bluetooth::audio::IsOffloadLeAudioConfigurationValid(session_type_,
+                 audioConfig.leAudioCodecConfig())) {
+    LOG(WARNING) << __func__ << " - Unsupported LC3 Offloaded Configuration="
+                 << toString(audioConfig.leAudioCodecConfig());
+    _hidl_cb(BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION,
+             DataMQ::Descriptor());
+    return Void();
+  }
+
+  return BluetoothAudioProvider::startSession_2_1(hostIf, audioConfig,
+                                                  _hidl_cb);
+}
+
+Return<void> LeAudioOffloadAudioProvider::onSessionReady(startSession_cb _hidl_cb) {
+  BluetoothAudioSessionReport_2_1::OnSessionStarted(session_type_, stack_iface_,
+                                                    nullptr, audio_config_);
+  _hidl_cb(BluetoothAudioStatus::SUCCESS, DataMQ::Descriptor());
+  return Void();
+}
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h
new file mode 100644
index 0000000..564e9a3
--- /dev/null
+++ b/bluetooth/audio/2.1/default/LeAudioOffloadAudioProvider.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+
+#include "BluetoothAudioProvider.h"
+
+namespace android {
+namespace hardware {
+namespace bluetooth {
+namespace audio {
+namespace V2_1 {
+namespace implementation {
+
+class LeAudioOffloadAudioProvider : public BluetoothAudioProvider {
+ public:
+  LeAudioOffloadAudioProvider();
+
+  bool isValid(const SessionType& sessionType) override;
+  bool isValid(const V2_0::SessionType& sessionType) override;
+
+  Return<void> startSession_2_1(const sp<V2_0::IBluetoothAudioPort>& hostIf,
+                                const AudioConfiguration& audioConfig,
+                                startSession_cb _hidl_cb) override;
+
+ private:
+  Return<void> onSessionReady(startSession_cb _hidl_cb) override;
+};
+
+class LeAudioOffloadOutputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+  LeAudioOffloadOutputAudioProvider();
+};
+
+class LeAudioOffloadInputAudioProvider : public LeAudioOffloadAudioProvider {
+ public:
+  LeAudioOffloadInputAudioProvider();
+};
+
+}  // namespace implementation
+}  // namespace V2_1
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace hardware
+}  // namespace android
diff --git a/bluetooth/audio/2.1/types.hal b/bluetooth/audio/2.1/types.hal
new file mode 100644
index 0000000..e0dcc02
--- /dev/null
+++ b/bluetooth/audio/2.1/types.hal
@@ -0,0 +1,110 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.bluetooth.audio@2.1;
+
+import @2.0::BitsPerSample;
+import @2.0::ChannelMode;
+import @2.0::CodecCapabilities;
+import @2.0::CodecConfiguration;
+import @2.0::CodecType;
+import @2.0::PcmParameters;
+import @2.0::SampleRate;
+import @2.0::SessionType;
+
+enum SessionType : @2.0::SessionType {
+    /** Used when encoded by Bluetooth Stack and streaming to LE Audio device */
+    LE_AUDIO_SOFTWARE_ENCODING_DATAPATH,
+    /** Used when decoded by Bluetooth Stack and streaming to audio framework */
+    LE_AUDIO_SOFTWARE_DECODED_DATAPATH,
+    /** Encoding is done by HW an there is control only */
+    LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH,
+    /** Decoding is done by HW an there is control only */
+    LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH,
+};
+
+enum CodecType : @2.0::CodecType {
+    LC3 = 0x20,
+};
+
+enum SampleRate : @2.0::SampleRate {
+    RATE_8000 = 0x100,
+    RATE_32000 = 0x200,
+};
+
+/** Used for Software Encoding audio feed parameters */
+struct PcmParameters {
+    SampleRate sampleRate;
+    ChannelMode channelMode;
+    BitsPerSample bitsPerSample;
+    /** Data interval for data transfer */
+    uint32_t dataIntervalUs;
+};
+
+enum Lc3FrameDuration : uint8_t {
+    DURATION_10000US = 0x00,
+    DURATION_7500US = 0x01,
+};
+
+/**
+ * Used for Hardware Encoding/Decoding LC3 codec parameters.
+ */
+struct Lc3Parameters {
+    /* PCM is Input for encoder, Output for decoder */
+    BitsPerSample pcmBitDepth;
+
+    /* codec-specific parameters */
+    SampleRate samplingFrequency;
+    Lc3FrameDuration frameDuration;
+    /* length in octets of a codec frame */
+    uint32_t octetsPerFrame;
+    /* Number of blocks of codec frames per single SDU (Service Data Unit) */
+    uint8_t blocksPerSdu;
+};
+
+/**
+ * Used to specify the capabilities of the LC3 codecs supported by Hardware Encoding.
+ */
+struct Lc3CodecCapabilities {
+    /* This is bitfield, if bit N is set, HW Offloader supports N+1 channels at the same time.
+     * Example: 0x27 = 0b00100111: One, two, three or six channels supported.*/
+    uint8_t supportedChannelCounts;
+    Lc3Parameters lc3Capabilities;
+};
+
+/** Used to specify the capabilities of the different session types */
+safe_union AudioCapabilities {
+    PcmParameters pcmCapabilities;
+    CodecCapabilities codecCapabilities;
+    Lc3CodecCapabilities leAudioCapabilities;
+};
+
+/**
+ * Used to configure a LC3 Hardware Encoding session.
+ */
+struct Lc3CodecConfiguration {
+    /* This is also bitfield, specifying how the channels are ordered in the outgoing media packet.
+     * Bit meaning is defined in Bluetooth Assigned Numbers. */
+    uint32_t audioChannelAllocation;
+    Lc3Parameters lc3Config;
+};
+
+/** Used to configure either a Hardware or Software Encoding session based on session type */
+safe_union AudioConfiguration {
+    PcmParameters pcmConfig;
+    CodecConfiguration codecConfig;
+    Lc3CodecConfiguration leAudioCodecConfig;
+};
\ No newline at end of file
diff --git a/bluetooth/audio/2.1/vts/OWNERS b/bluetooth/audio/2.1/vts/OWNERS
new file mode 100644
index 0000000..b6c0813
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/OWNERS
@@ -0,0 +1,3 @@
+include platform/system/bt:/OWNERS
+
+cheneyni@google.com
diff --git a/bluetooth/audio/2.1/vts/functional/Android.bp b/bluetooth/audio/2.1/vts/functional/Android.bp
new file mode 100644
index 0000000..3314a8a
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/functional/Android.bp
@@ -0,0 +1,23 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalBluetoothAudioV2_1TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBluetoothAudioV2_1TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.audio.common@5.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "android.hardware.bluetooth.audio@2.0",
+    ],
+    shared_libs: [
+        "libfmq",
+    ],
+    test_suites: ["general-tests", "vts"],
+}
diff --git a/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
new file mode 100644
index 0000000..57fa07b
--- /dev/null
+++ b/bluetooth/audio/2.1/vts/functional/VtsHalBluetoothAudioV2_1TargetTest.cpp
@@ -0,0 +1,1217 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "bluetooth_audio_hidl_hal_test"
+
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/logging.h>
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvider.h>
+#include <android/hardware/bluetooth/audio/2.1/IBluetoothAudioProvidersFactory.h>
+#include <fmq/MessageQueue.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/ServiceManagement.h>
+#include <utils/Log.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::AacObjectType;
+using ::android::hardware::bluetooth::audio::V2_0::AacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AacVariableBitRate;
+using ::android::hardware::bluetooth::audio::V2_0::AptxParameters;
+using ::android::hardware::bluetooth::audio::V2_0::AudioCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecCapabilities;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioProvider;
+using ::android::hardware::bluetooth::audio::V2_0::LdacChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::LdacParameters;
+using ::android::hardware::bluetooth::audio::V2_0::LdacQualityIndex;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SbcAllocMethod;
+using ::android::hardware::bluetooth::audio::V2_0::SbcBlockLength;
+using ::android::hardware::bluetooth::audio::V2_0::SbcChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::SbcNumSubbands;
+using ::android::hardware::bluetooth::audio::V2_0::SbcParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+using ::android::hardware::bluetooth::audio::V2_1::
+    IBluetoothAudioProvidersFactory;
+using ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+using CodecSpecificConfig = ::android::hardware::bluetooth::audio::V2_0::
+    CodecConfiguration::CodecSpecific;
+
+namespace {
+constexpr android::hardware::bluetooth::audio::V2_0::SampleRate
+    a2dp_sample_rates[5] = {
+        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
+        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_44100,
+        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_48000,
+        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_88200,
+        android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_96000};
+constexpr BitsPerSample a2dp_bits_per_samples[4] = {
+    BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16, BitsPerSample::BITS_24,
+    BitsPerSample::BITS_32};
+constexpr ChannelMode a2dp_channel_modes[3] = {
+    ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+constexpr CodecType a2dp_codec_types[6] = {CodecType::UNKNOWN, CodecType::SBC,
+                                           CodecType::AAC,     CodecType::APTX,
+                                           CodecType::APTX_HD, CodecType::LDAC};
+
+template <typename T>
+std::vector<T> ExtractValuesFromBitmask(T bitmasks, uint32_t bitfield,
+                                        bool supported) {
+  std::vector<T> retval;
+  if (!supported) {
+    retval.push_back(static_cast<T>(bitfield));
+  }
+  uint32_t test_bit = 0x00000001;
+  while (test_bit <= static_cast<uint32_t>(bitmasks) && test_bit <= bitfield) {
+    if ((bitfield & test_bit)) {
+      if ((!(bitmasks & test_bit) && !supported) ||
+          ((bitmasks & test_bit) && supported)) {
+        retval.push_back(static_cast<T>(test_bit));
+      }
+    }
+    if (test_bit == 0x80000000) {
+      break;
+    }
+    test_bit <<= 1;
+  }
+  return retval;
+}
+}  // namespace
+
+// The base test class for Bluetooth Audio HAL.
+class BluetoothAudioProvidersFactoryHidlTest
+    : public ::testing::TestWithParam<std::string> {
+ public:
+  virtual void SetUp() override {
+    providers_factory_ =
+        IBluetoothAudioProvidersFactory::getService(GetParam());
+    ASSERT_NE(providers_factory_, nullptr);
+  }
+
+  virtual void TearDown() override { providers_factory_ = nullptr; }
+
+  // A simple test implementation of IBluetoothAudioPort.
+  class BluetoothAudioPort : public ::testing::VtsHalHidlTargetCallbackBase<
+                                 BluetoothAudioProvidersFactoryHidlTest>,
+                             public IBluetoothAudioPort {
+    BluetoothAudioProvidersFactoryHidlTest& parent_;
+
+   public:
+    BluetoothAudioPort(BluetoothAudioProvidersFactoryHidlTest& parent)
+        : parent_(parent) {}
+    virtual ~BluetoothAudioPort() = default;
+
+    Return<void> startStream() override {
+      parent_.audio_provider_->streamStarted(BluetoothAudioStatus::SUCCESS);
+      return Void();
+    }
+
+    Return<void> suspendStream() override {
+      parent_.audio_provider_->streamSuspended(BluetoothAudioStatus::SUCCESS);
+      return Void();
+    }
+
+    Return<void> stopStream() override { return Void(); }
+
+    Return<void> getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
+      _hidl_cb(BluetoothAudioStatus::SUCCESS, 0, 0, {.tvSec = 0, .tvNSec = 0});
+      return Void();
+    }
+
+    Return<void> updateMetadata(const SourceMetadata& sourceMetadata __unused) {
+      return Void();
+    }
+  };
+
+  void GetProviderCapabilitiesHelper(const SessionType& session_type) {
+    temp_provider_capabilities_.clear();
+    auto hidl_cb = [&temp_capabilities = this->temp_provider_capabilities_](
+                       const hidl_vec<AudioCapabilities>& audioCapabilities) {
+      for (auto audioCapability : audioCapabilities)
+        temp_capabilities.push_back(audioCapability);
+    };
+    auto hidl_retval =
+        providers_factory_->getProviderCapabilities(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    if (session_type == SessionType::UNKNOWN) {
+      ASSERT_TRUE(temp_provider_capabilities_.empty());
+    } else if (session_type != SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+      // All software paths are mandatory and must have exact 1 "PcmParameters"
+      ASSERT_EQ(temp_provider_capabilities_.size(), 1);
+      ASSERT_EQ(temp_provider_capabilities_[0].getDiscriminator(),
+                AudioCapabilities::hidl_discriminator::pcmCapabilities);
+    } else {
+      uint32_t codec_type_bitmask = 0x00000000;
+      // empty capability means offload is unsupported
+      for (auto audio_capability : temp_provider_capabilities_) {
+        ASSERT_EQ(audio_capability.getDiscriminator(),
+                  AudioCapabilities::hidl_discriminator::codecCapabilities);
+        const CodecCapabilities& codec_capabilities =
+            audio_capability.codecCapabilities();
+        // Every codec can present once at most
+        ASSERT_EQ(codec_type_bitmask &
+                      static_cast<uint32_t>(codec_capabilities.codecType),
+                  0);
+        switch (codec_capabilities.codecType) {
+          case CodecType::SBC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          sbcCapabilities);
+            break;
+          case CodecType::AAC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          aacCapabilities);
+            break;
+          case CodecType::APTX:
+            FALLTHROUGH_INTENDED;
+          case CodecType::APTX_HD:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          aptxCapabilities);
+            break;
+          case CodecType::LDAC:
+            ASSERT_EQ(codec_capabilities.capabilities.getDiscriminator(),
+                      CodecCapabilities::Capabilities::hidl_discriminator::
+                          ldacCapabilities);
+            break;
+          case CodecType::UNKNOWN:
+            break;
+        }
+        codec_type_bitmask |= codec_capabilities.codecType;
+      }
+    }
+  }
+
+  void GetProviderCapabilitiesHelper_2_1(
+      const android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type) {
+    temp_provider_capabilities_2_1_.clear();
+    auto hidl_cb =
+        [&temp_capabilities = this->temp_provider_capabilities_2_1_](
+            const hidl_vec<
+                android::hardware::bluetooth::audio::V2_1::AudioCapabilities>&
+                audioCapabilities) {
+          for (auto audioCapability : audioCapabilities)
+            temp_capabilities.push_back(audioCapability);
+        };
+    auto hidl_retval =
+        providers_factory_->getProviderCapabilities_2_1(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+
+    // All software paths are mandatory and must have exact 1 "PcmParameters"
+    ASSERT_EQ(temp_provider_capabilities_2_1_.size(), 1);
+    ASSERT_EQ(temp_provider_capabilities_2_1_[0].getDiscriminator(),
+              android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
+                  hidl_discriminator::pcmCapabilities);
+  }
+
+  // This helps to open the specified provider and check the openProvider()
+  // has corruct return values. BUT, to keep it simple, it does not consider
+  // the capability, and please do so at the SetUp of each session's test.
+  void OpenProviderHelper(const SessionType& session_type) {
+    BluetoothAudioStatus cb_status;
+    auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_](
+                       BluetoothAudioStatus status,
+                       const sp<IBluetoothAudioProvider>& provider) {
+      cb_status = status;
+      local_provider = provider;
+    };
+    auto hidl_retval = providers_factory_->openProvider(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    if (cb_status == BluetoothAudioStatus::SUCCESS) {
+      ASSERT_NE(session_type, SessionType::UNKNOWN);
+      ASSERT_NE(audio_provider_, nullptr);
+      audio_port_ = new BluetoothAudioPort(*this);
+    } else {
+      // A2DP_HARDWARE_OFFLOAD_DATAPATH is optional
+      ASSERT_TRUE(session_type == SessionType::UNKNOWN ||
+                  session_type == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+      ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+      ASSERT_EQ(audio_provider_, nullptr);
+    }
+  }
+
+  // This helps to open the specified provider and check the openProvider_2_1()
+  // has corruct return values. BUT, to keep it simple, it does not consider
+  // the capability, and please do so at the SetUp of each session's test.
+  void OpenProviderHelper_2_1(
+      const android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type) {
+    BluetoothAudioStatus cb_status;
+    auto hidl_cb = [&cb_status, &local_provider = this->audio_provider_2_1_](
+                       BluetoothAudioStatus status,
+                       const sp<android::hardware::bluetooth::audio::V2_1::
+                                    IBluetoothAudioProvider>& provider) {
+      cb_status = status;
+      local_provider = provider;
+    };
+    auto hidl_retval =
+        providers_factory_->openProvider_2_1(session_type, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    if (cb_status == BluetoothAudioStatus::SUCCESS) {
+      ASSERT_NE(
+          session_type,
+          android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
+      ASSERT_NE(audio_provider_2_1_, nullptr);
+      audio_port_ = new BluetoothAudioPort(*this);
+    } else {
+      ASSERT_TRUE(
+          session_type ==
+          android::hardware::bluetooth::audio::V2_1::SessionType::UNKNOWN);
+      ASSERT_EQ(cb_status, BluetoothAudioStatus::FAILURE);
+      ASSERT_EQ(audio_provider_2_1_, nullptr);
+    }
+  }
+
+  bool IsPcmParametersSupported(const PcmParameters& pcm_parameters) {
+    if (temp_provider_capabilities_.size() != 1 ||
+        temp_provider_capabilities_[0].getDiscriminator() !=
+            AudioCapabilities::hidl_discriminator::pcmCapabilities) {
+      return false;
+    }
+    auto pcm_capability = temp_provider_capabilities_[0].pcmCapabilities();
+    bool is_parameter_valid =
+        (pcm_parameters.sampleRate != android::hardware::bluetooth::audio::
+                                          V2_0::SampleRate::RATE_UNKNOWN &&
+         pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+         pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN);
+    bool is_parameter_in_capability =
+        (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+         pcm_capability.channelMode & pcm_parameters.channelMode &&
+         pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+    return is_parameter_valid && is_parameter_in_capability;
+  }
+
+  bool IsPcmParametersSupported_2_1(
+      const android::hardware::bluetooth::audio::V2_1::PcmParameters&
+          pcm_parameters) {
+    if (temp_provider_capabilities_2_1_.size() != 1 ||
+        temp_provider_capabilities_2_1_[0].getDiscriminator() !=
+            android::hardware::bluetooth::audio::V2_1::AudioCapabilities::
+                hidl_discriminator::pcmCapabilities) {
+      return false;
+    }
+    auto pcm_capability = temp_provider_capabilities_2_1_[0].pcmCapabilities();
+    bool is_parameter_valid =
+        (pcm_parameters.sampleRate != SampleRate::RATE_UNKNOWN &&
+         pcm_parameters.channelMode != ChannelMode::UNKNOWN &&
+         pcm_parameters.bitsPerSample != BitsPerSample::BITS_UNKNOWN &&
+         pcm_parameters.dataIntervalUs != 0);
+    bool is_parameter_in_capability =
+        (pcm_capability.sampleRate & pcm_parameters.sampleRate &&
+         pcm_capability.channelMode & pcm_parameters.channelMode &&
+         pcm_capability.bitsPerSample & pcm_parameters.bitsPerSample);
+    return is_parameter_valid && is_parameter_in_capability;
+  }
+
+  sp<IBluetoothAudioProvidersFactory> providers_factory_;
+
+  // temp storage saves the specified provider capability by
+  // GetProviderCapabilitiesHelper()
+  std::vector<AudioCapabilities> temp_provider_capabilities_;
+  std::vector<android::hardware::bluetooth::audio::V2_1::AudioCapabilities>
+      temp_provider_capabilities_2_1_;
+
+  // audio_provider_ is for the Bluetooth stack to report session started/ended
+  // and handled audio stream started / suspended
+  sp<IBluetoothAudioProvider> audio_provider_;
+  sp<android::hardware::bluetooth::audio::V2_1::IBluetoothAudioProvider>
+      audio_provider_2_1_;
+
+  // audio_port_ is for the Audio HAL to send stream start/suspend/stop commands
+  // to Bluetooth stack
+  sp<IBluetoothAudioPort> audio_port_;
+
+  static constexpr SessionType session_types_[4] = {
+      SessionType::UNKNOWN, SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH,
+      SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH,
+      SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH};
+};
+
+/**
+ * Test whether we can get the FactoryService from HIDL
+ */
+TEST_P(BluetoothAudioProvidersFactoryHidlTest, GetProvidersFactoryService) {}
+
+/**
+ * Test whether we can open a provider for each provider returned by
+ * getProviderCapabilities() with non-empty capabalities
+ */
+TEST_P(BluetoothAudioProvidersFactoryHidlTest,
+       OpenProviderAndCheckCapabilitiesBySession) {
+  for (auto session_type : session_types_) {
+    GetProviderCapabilitiesHelper(session_type);
+    OpenProviderHelper(session_type);
+    // We must be able to open a provider if its getProviderCapabilities()
+    // returns non-empty list.
+    EXPECT_TRUE(temp_provider_capabilities_.empty() ||
+                audio_provider_ != nullptr);
+  }
+}
+
+/**
+ * openProvider A2DP_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderA2dpSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+    OpenProviderHelper(SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH);
+    ASSERT_NE(audio_provider_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_P(BluetoothAudioProviderA2dpSoftwareHidlTest, OpenA2dpSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH can be started and stopped with
+ * different PCM config
+ */
+TEST_P(BluetoothAudioProviderA2dpSoftwareHidlTest,
+       StartAndEndA2dpSoftwareSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    }
+  };
+  AudioConfiguration audio_config = {};
+  PcmParameters pcm_parameters = {};
+  for (auto sample_rate : a2dp_sample_rates) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : a2dp_bits_per_samples) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : a2dp_channel_modes) {
+        pcm_parameters.channelMode = channel_mode;
+        is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+        audio_config.pcmConfig(pcm_parameters);
+        auto hidl_retval =
+            audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+        // HIDL calls should not be failed and callback has to be executed
+        ASSERT_TRUE(hidl_retval.isOk());
+        if (is_codec_config_valid) {
+          EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+        }
+        EXPECT_TRUE(audio_provider_->endSession().isOk());
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
+/**
+ * openProvider A2DP_HARDWARE_OFFLOAD_DATAPATH
+ */
+class BluetoothAudioProviderA2dpHardwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    OpenProviderHelper(SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+    ASSERT_TRUE(temp_provider_capabilities_.empty() ||
+                audio_provider_ != nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  bool IsOffloadSupported() { return (temp_provider_capabilities_.size() > 0); }
+
+  void GetOffloadCodecCapabilityHelper(const CodecType& codec_type) {
+    temp_codec_capabilities_ = {};
+    for (auto codec_capability : temp_provider_capabilities_) {
+      if (codec_capability.codecCapabilities().codecType != codec_type) {
+        continue;
+      }
+      temp_codec_capabilities_ = codec_capability.codecCapabilities();
+    }
+  }
+
+  std::vector<CodecSpecificConfig> GetSbcCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> sbc_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::SBC);
+    if (temp_codec_capabilities_.codecType != CodecType::SBC) {
+      return sbc_codec_specifics;
+    }
+    // parse the capability
+    SbcParameters sbc_capability =
+        temp_codec_capabilities_.capabilities.sbcCapabilities();
+    if (sbc_capability.minBitpool > sbc_capability.maxBitpool) {
+      return sbc_codec_specifics;
+    }
+    std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+        sample_rates = ExtractValuesFromBitmask<
+            android::hardware::bluetooth::audio::V2_0::SampleRate>(
+            sbc_capability.sampleRate, 0xff, supported);
+    std::vector<SbcChannelMode> channel_modes =
+        ExtractValuesFromBitmask<SbcChannelMode>(sbc_capability.channelMode,
+                                                 0x0f, supported);
+    std::vector<SbcBlockLength> block_lengths =
+        ExtractValuesFromBitmask<SbcBlockLength>(sbc_capability.blockLength,
+                                                 0xf0, supported);
+    std::vector<SbcNumSubbands> num_subbandss =
+        ExtractValuesFromBitmask<SbcNumSubbands>(sbc_capability.numSubbands,
+                                                 0x0c, supported);
+    std::vector<SbcAllocMethod> alloc_methods =
+        ExtractValuesFromBitmask<SbcAllocMethod>(sbc_capability.allocMethod,
+                                                 0x03, supported);
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(sbc_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    SbcParameters sbc_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto block_length : block_lengths) {
+          for (auto num_subbands : num_subbandss) {
+            for (auto alloc_method : alloc_methods) {
+              for (auto bits_per_sample : bits_per_samples) {
+                sbc_data = {.sampleRate = sample_rate,
+                            .channelMode = channel_mode,
+                            .blockLength = block_length,
+                            .numSubbands = num_subbands,
+                            .allocMethod = alloc_method,
+                            .bitsPerSample = bits_per_sample,
+                            .minBitpool = sbc_capability.minBitpool,
+                            .maxBitpool = sbc_capability.maxBitpool};
+                codec_specific.sbcConfig(sbc_data);
+                sbc_codec_specifics.push_back(codec_specific);
+              }
+            }
+          }
+        }
+      }
+    }
+    return sbc_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetAacCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> aac_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::AAC);
+    if (temp_codec_capabilities_.codecType != CodecType::AAC) {
+      return aac_codec_specifics;
+    }
+    // parse the capability
+    AacParameters aac_capability =
+        temp_codec_capabilities_.capabilities.aacCapabilities();
+    std::vector<AacObjectType> object_types =
+        ExtractValuesFromBitmask<AacObjectType>(aac_capability.objectType, 0xf0,
+                                                supported);
+    std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+        sample_rates = ExtractValuesFromBitmask<
+            android::hardware::bluetooth::audio::V2_0::SampleRate>(
+            aac_capability.sampleRate, 0xff, supported);
+    std::vector<ChannelMode> channel_modes =
+        ExtractValuesFromBitmask<ChannelMode>(aac_capability.channelMode, 0x03,
+                                              supported);
+    std::vector<AacVariableBitRate> variable_bit_rate_enableds = {
+        AacVariableBitRate::DISABLED};
+    if (aac_capability.variableBitRateEnabled == AacVariableBitRate::ENABLED) {
+      variable_bit_rate_enableds.push_back(AacVariableBitRate::ENABLED);
+    }
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(aac_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    AacParameters aac_data;
+    for (auto object_type : object_types) {
+      for (auto sample_rate : sample_rates) {
+        for (auto channel_mode : channel_modes) {
+          for (auto variable_bit_rate_enabled : variable_bit_rate_enableds) {
+            for (auto bits_per_sample : bits_per_samples) {
+              aac_data = {.objectType = object_type,
+                          .sampleRate = sample_rate,
+                          .channelMode = channel_mode,
+                          .variableBitRateEnabled = variable_bit_rate_enabled,
+                          .bitsPerSample = bits_per_sample};
+              codec_specific.aacConfig(aac_data);
+              aac_codec_specifics.push_back(codec_specific);
+            }
+          }
+        }
+      }
+    }
+    return aac_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetLdacCodecSpecificSupportedList(
+      bool supported) {
+    std::vector<CodecSpecificConfig> ldac_codec_specifics;
+    GetOffloadCodecCapabilityHelper(CodecType::LDAC);
+    if (temp_codec_capabilities_.codecType != CodecType::LDAC) {
+      return ldac_codec_specifics;
+    }
+    // parse the capability
+    LdacParameters ldac_capability =
+        temp_codec_capabilities_.capabilities.ldacCapabilities();
+    std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+        sample_rates = ExtractValuesFromBitmask<
+            android::hardware::bluetooth::audio::V2_0::SampleRate>(
+            ldac_capability.sampleRate, 0xff, supported);
+    std::vector<LdacChannelMode> channel_modes =
+        ExtractValuesFromBitmask<LdacChannelMode>(ldac_capability.channelMode,
+                                                  0x07, supported);
+    std::vector<LdacQualityIndex> quality_indexes = {
+        LdacQualityIndex::QUALITY_HIGH, LdacQualityIndex::QUALITY_MID,
+        LdacQualityIndex::QUALITY_LOW, LdacQualityIndex::QUALITY_ABR};
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(ldac_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    LdacParameters ldac_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto quality_index : quality_indexes) {
+          for (auto bits_per_sample : bits_per_samples) {
+            ldac_data = {.sampleRate = sample_rate,
+                         .channelMode = channel_mode,
+                         .qualityIndex = quality_index,
+                         .bitsPerSample = bits_per_sample};
+            codec_specific.ldacConfig(ldac_data);
+            ldac_codec_specifics.push_back(codec_specific);
+          }
+        }
+      }
+    }
+    return ldac_codec_specifics;
+  }
+
+  std::vector<CodecSpecificConfig> GetAptxCodecSpecificSupportedList(
+      bool is_hd, bool supported) {
+    std::vector<CodecSpecificConfig> aptx_codec_specifics;
+    GetOffloadCodecCapabilityHelper(
+        (is_hd ? CodecType::APTX_HD : CodecType::APTX));
+    if ((is_hd && temp_codec_capabilities_.codecType != CodecType::APTX_HD) ||
+        (!is_hd && temp_codec_capabilities_.codecType != CodecType::APTX)) {
+      return aptx_codec_specifics;
+    }
+    // parse the capability
+    AptxParameters aptx_capability =
+        temp_codec_capabilities_.capabilities.aptxCapabilities();
+    std::vector<android::hardware::bluetooth::audio::V2_0::SampleRate>
+        sample_rates = ExtractValuesFromBitmask<
+            android::hardware::bluetooth::audio::V2_0::SampleRate>(
+            aptx_capability.sampleRate, 0xff, supported);
+    std::vector<ChannelMode> channel_modes =
+        ExtractValuesFromBitmask<ChannelMode>(aptx_capability.channelMode, 0x03,
+                                              supported);
+    std::vector<BitsPerSample> bits_per_samples =
+        ExtractValuesFromBitmask<BitsPerSample>(aptx_capability.bitsPerSample,
+                                                0x07, supported);
+    // combine those parameters into one list of
+    // CodecConfiguration::CodecSpecific
+    CodecSpecificConfig codec_specific = {};
+    AptxParameters aptx_data;
+    for (auto sample_rate : sample_rates) {
+      for (auto channel_mode : channel_modes) {
+        for (auto bits_per_sample : bits_per_samples) {
+          aptx_data = {.sampleRate = sample_rate,
+                       .channelMode = channel_mode,
+                       .bitsPerSample = bits_per_sample};
+          codec_specific.aptxConfig(aptx_data);
+          aptx_codec_specifics.push_back(codec_specific);
+        }
+      }
+    }
+    return aptx_codec_specifics;
+  }
+
+  // temp storage saves the specified codec capability by
+  // GetOffloadCodecCapabilityHelper()
+  CodecCapabilities temp_codec_capabilities_;
+};
+
+/**
+ * Test whether we can open a provider of type
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest, OpenA2dpHardwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * SBC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpSbcHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::SBC;
+  codec_config.encodedAudioBitrate = 328000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> sbc_codec_specifics =
+      GetSbcCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : sbc_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AAC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpAacHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::AAC;
+  codec_config.encodedAudioBitrate = 320000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> aac_codec_specifics =
+      GetAacCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : aac_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * LDAC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpLdacHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  CodecConfiguration codec_config = {};
+  codec_config.codecType = CodecType::LDAC;
+  codec_config.encodedAudioBitrate = 990000;
+  codec_config.peerMtu = 1005;
+  codec_config.isScmstEnabled = false;
+  AudioConfiguration audio_config = {};
+  std::vector<CodecSpecificConfig> ldac_codec_specifics =
+      GetLdacCodecSpecificSupportedList(true);
+  auto hidl_cb = [](BluetoothAudioStatus status,
+                    const DataMQ::Descriptor& dataMQ) {
+    EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+    EXPECT_FALSE(dataMQ.isHandleValid());
+  };
+  for (auto codec_specific : ldac_codec_specifics) {
+    codec_config.config = codec_specific;
+    audio_config.codecConfig(codec_config);
+    auto hidl_retval =
+        audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+    // HIDL calls should not be failed and callback has to be executed
+    ASSERT_TRUE(hidl_retval.isOk());
+    EXPECT_TRUE(audio_provider_->endSession().isOk());
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * AptX hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpAptxHardwareSession) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+
+  for (auto codec_type : {CodecType::APTX, CodecType::APTX_HD}) {
+    CodecConfiguration codec_config = {};
+    codec_config.codecType = codec_type;
+    codec_config.encodedAudioBitrate =
+        (codec_type == CodecType::APTX ? 352000 : 576000);
+    codec_config.peerMtu = 1005;
+    codec_config.isScmstEnabled = false;
+    AudioConfiguration audio_config = {};
+    std::vector<CodecSpecificConfig> aptx_codec_specifics =
+        GetAptxCodecSpecificSupportedList(
+            (codec_type == CodecType::APTX_HD ? true : false), true);
+    auto hidl_cb = [](BluetoothAudioStatus status,
+                      const DataMQ::Descriptor& dataMQ) {
+      EXPECT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    };
+    for (auto codec_specific : aptx_codec_specifics) {
+      codec_config.config = codec_specific;
+      audio_config.codecConfig(codec_config);
+      auto hidl_retval =
+          audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+      // HIDL calls should not be failed and callback has to be executed
+      ASSERT_TRUE(hidl_retval.isOk());
+      EXPECT_TRUE(audio_provider_->endSession().isOk());
+    }
+  }
+}
+
+/**
+ * Test whether each provider of type
+ * SessionType::A2DP_HARDWARE_ENCODING_DATAPATH can be started and stopped with
+ * an invalid codec config
+ */
+TEST_P(BluetoothAudioProviderA2dpHardwareHidlTest,
+       StartAndEndA2dpHardwareSessionInvalidCodecConfig) {
+  if (!IsOffloadSupported()) {
+    return;
+  }
+  ASSERT_NE(audio_provider_, nullptr);
+
+  std::vector<CodecSpecificConfig> codec_specifics;
+  for (auto codec_type : a2dp_codec_types) {
+    switch (codec_type) {
+      case CodecType::SBC:
+        codec_specifics = GetSbcCodecSpecificSupportedList(false);
+        break;
+      case CodecType::AAC:
+        codec_specifics = GetAacCodecSpecificSupportedList(false);
+        break;
+      case CodecType::LDAC:
+        codec_specifics = GetLdacCodecSpecificSupportedList(false);
+        break;
+      case CodecType::APTX:
+        codec_specifics = GetAptxCodecSpecificSupportedList(false, false);
+        break;
+      case CodecType::APTX_HD:
+        codec_specifics = GetAptxCodecSpecificSupportedList(true, false);
+        break;
+      case CodecType::UNKNOWN:
+        codec_specifics.clear();
+        break;
+    }
+    if (codec_specifics.empty()) {
+      continue;
+    }
+
+    CodecConfiguration codec_config = {};
+    codec_config.codecType = codec_type;
+    codec_config.encodedAudioBitrate = 328000;
+    codec_config.peerMtu = 1005;
+    codec_config.isScmstEnabled = false;
+    AudioConfiguration audio_config = {};
+    auto hidl_cb = [](BluetoothAudioStatus status,
+                      const DataMQ::Descriptor& dataMQ) {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    };
+    for (auto codec_specific : codec_specifics) {
+      codec_config.config = codec_specific;
+      audio_config.codecConfig(codec_config);
+      auto hidl_retval =
+          audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+      // HIDL calls should not be failed and callback has to be executed
+      ASSERT_TRUE(hidl_retval.isOk());
+      EXPECT_TRUE(audio_provider_->endSession().isOk());
+    }
+  }
+}
+
+/**
+ * openProvider HEARING_AID_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderHearingAidSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper(
+        SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+    OpenProviderHelper(SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+    ASSERT_NE(audio_provider_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  static constexpr android::hardware::bluetooth::audio::V2_0::SampleRate
+      hearing_aid_sample_rates_[3] = {
+          android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_UNKNOWN,
+          android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_16000,
+          android::hardware::bluetooth::audio::V2_0::SampleRate::RATE_24000};
+  static constexpr BitsPerSample hearing_aid_bits_per_samples_[3] = {
+      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+      BitsPerSample::BITS_24};
+  static constexpr ChannelMode hearing_aid_channel_modes_[3] = {
+      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_HARDWARE_ENCODING_DATAPATH can be started and
+ * stopped with SBC hardware encoding config
+ */
+TEST_P(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+       OpenHearingAidSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderHearingAidSoftwareHidlTest,
+       StartAndEndHearingAidSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+    }
+  };
+  AudioConfiguration audio_config = {};
+  PcmParameters pcm_parameters = {};
+  for (auto sample_rate : hearing_aid_sample_rates_) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : hearing_aid_bits_per_samples_) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : hearing_aid_channel_modes_) {
+        pcm_parameters.channelMode = channel_mode;
+        is_codec_config_valid = IsPcmParametersSupported(pcm_parameters);
+        audio_config.pcmConfig(pcm_parameters);
+        auto hidl_retval =
+            audio_provider_->startSession(audio_port_, audio_config, hidl_cb);
+        // HIDL calls should not be failed and callback has to be executed
+        ASSERT_TRUE(hidl_retval.isOk());
+        if (is_codec_config_valid) {
+          EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+        }
+        EXPECT_TRUE(audio_provider_->endSession().isOk());
+      }  // ChannelMode
+    }    // BitsPerSampple
+  }      // SampleRate
+}
+
+/**
+ * openProvider LE_AUDIO_SOFTWARE_ENCODING_DATAPATH
+ */
+class BluetoothAudioProviderLeAudioOutputSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper_2_1(
+        android::hardware::bluetooth::audio::V2_1::SessionType::
+            LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
+    OpenProviderHelper_2_1(
+        android::hardware::bluetooth::audio::V2_1::SessionType::
+            LE_AUDIO_SOFTWARE_ENCODING_DATAPATH);
+    ASSERT_NE(audio_provider_2_1_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_2_1_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  static constexpr SampleRate le_audio_output_sample_rates_[11] = {
+      SampleRate::RATE_UNKNOWN, SampleRate::RATE_8000,  SampleRate::RATE_16000,
+      SampleRate::RATE_24000,   SampleRate::RATE_32000, SampleRate::RATE_44100,
+      SampleRate::RATE_48000};
+  static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
+      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+      BitsPerSample::BITS_24};
+  static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
+      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+  static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
+      0 /* Invalid */, 10000 /* Valid 10ms */};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped
+ */
+TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+       OpenLeAudioOutputSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+       DISABLED_StartAndEndLeAudioOutputSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+      tempDataMQ.reset(nullptr);
+    }
+  };
+  android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
+      {};
+  android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
+  for (auto sample_rate : le_audio_output_sample_rates_) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : le_audio_output_channel_modes_) {
+        pcm_parameters.channelMode = channel_mode;
+        for (auto data_interval_us : le_audio_output_data_interval_us_) {
+          pcm_parameters.dataIntervalUs = data_interval_us;
+          is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
+          audio_config.pcmConfig(pcm_parameters);
+          auto hidl_retval = audio_provider_2_1_->startSession_2_1(
+              audio_port_, audio_config, hidl_cb);
+          // HIDL calls should not be failed and callback has to be executed
+          ASSERT_TRUE(hidl_retval.isOk());
+          if (is_codec_config_valid) {
+            EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+          } else {
+            EXPECT_TRUE(tempDataMQ == nullptr);
+          }
+          EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
+        }  // uint32_t (data interval in microseconds)
+      }    // ChannelMode
+    }      // BitsPerSampple
+  }        // SampleRate
+}
+
+/**
+ * openProvider LE_AUDIO_SOFTWARE_DECODED_DATAPATH
+ */
+class BluetoothAudioProviderLeAudioInputSoftwareHidlTest
+    : public BluetoothAudioProvidersFactoryHidlTest {
+ public:
+  virtual void SetUp() override {
+    BluetoothAudioProvidersFactoryHidlTest::SetUp();
+    GetProviderCapabilitiesHelper_2_1(
+        android::hardware::bluetooth::audio::V2_1::SessionType::
+            LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+    OpenProviderHelper_2_1(android::hardware::bluetooth::audio::V2_1::
+                               SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+    ASSERT_NE(audio_provider_2_1_, nullptr);
+  }
+
+  virtual void TearDown() override {
+    audio_port_ = nullptr;
+    audio_provider_2_1_ = nullptr;
+    BluetoothAudioProvidersFactoryHidlTest::TearDown();
+  }
+
+  static constexpr SampleRate le_audio_output_sample_rates_[11] = {
+      SampleRate::RATE_UNKNOWN, SampleRate::RATE_8000,  SampleRate::RATE_16000,
+      SampleRate::RATE_24000,   SampleRate::RATE_32000, SampleRate::RATE_44100,
+      SampleRate::RATE_48000};
+  static constexpr BitsPerSample le_audio_output_bits_per_samples_[3] = {
+      BitsPerSample::BITS_UNKNOWN, BitsPerSample::BITS_16,
+      BitsPerSample::BITS_24};
+  static constexpr ChannelMode le_audio_output_channel_modes_[3] = {
+      ChannelMode::UNKNOWN, ChannelMode::MONO, ChannelMode::STEREO};
+  static constexpr uint32_t le_audio_output_data_interval_us_[2] = {
+      0 /* Invalid */, 10000 /* Valid 10ms */};
+};
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
+ * stopped
+ */
+TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+       OpenLeAudioInputSoftwareProvider) {}
+
+/**
+ * Test whether each provider of type
+ * SessionType::LE_AUDIO_SOFTWARE_DECODED_DATAPATH can be started and
+ * stopped with different PCM config
+ */
+TEST_P(BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+       DISABLED_StartAndEndLeAudioInputSessionWithPossiblePcmConfig) {
+  bool is_codec_config_valid;
+  std::unique_ptr<DataMQ> tempDataMQ;
+  auto hidl_cb = [&is_codec_config_valid, &tempDataMQ](
+                     BluetoothAudioStatus status,
+                     const DataMQ::Descriptor& dataMQ) {
+    if (is_codec_config_valid) {
+      ASSERT_EQ(status, BluetoothAudioStatus::SUCCESS);
+      ASSERT_TRUE(dataMQ.isHandleValid());
+      tempDataMQ.reset(new DataMQ(dataMQ));
+    } else {
+      EXPECT_EQ(status, BluetoothAudioStatus::UNSUPPORTED_CODEC_CONFIGURATION);
+      EXPECT_FALSE(dataMQ.isHandleValid());
+      tempDataMQ.reset(nullptr);
+    }
+  };
+  android::hardware::bluetooth::audio::V2_1::AudioConfiguration audio_config =
+      {};
+  android::hardware::bluetooth::audio::V2_1::PcmParameters pcm_parameters = {};
+  for (auto sample_rate : le_audio_output_sample_rates_) {
+    pcm_parameters.sampleRate = sample_rate;
+    for (auto bits_per_sample : le_audio_output_bits_per_samples_) {
+      pcm_parameters.bitsPerSample = bits_per_sample;
+      for (auto channel_mode : le_audio_output_channel_modes_) {
+        pcm_parameters.channelMode = channel_mode;
+        for (auto data_interval_us : le_audio_output_data_interval_us_) {
+          pcm_parameters.dataIntervalUs = data_interval_us;
+          is_codec_config_valid = IsPcmParametersSupported_2_1(pcm_parameters);
+          audio_config.pcmConfig(pcm_parameters);
+          auto hidl_retval = audio_provider_2_1_->startSession_2_1(
+              audio_port_, audio_config, hidl_cb);
+          // HIDL calls should not be failed and callback has to be executed
+          ASSERT_TRUE(hidl_retval.isOk());
+          if (is_codec_config_valid) {
+            EXPECT_TRUE(tempDataMQ != nullptr && tempDataMQ->isValid());
+          } else {
+            EXPECT_TRUE(tempDataMQ == nullptr);
+          }
+          EXPECT_TRUE(audio_provider_2_1_->endSession().isOk());
+        }  // uint32_t (data interval in microseconds)
+      }    // ChannelMode
+    }      // BitsPerSampple
+  }        // SampleRate
+}
+
+static const std::vector<std::string> kAudioInstances =
+    android::hardware::getAllHalInstanceNames(
+        IBluetoothAudioProvidersFactory::descriptor);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProvidersFactoryHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, BluetoothAudioProvidersFactoryHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderA2dpSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+                         BluetoothAudioProviderA2dpSoftwareHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderA2dpHardwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+                         BluetoothAudioProviderA2dpHardwareHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderHearingAidSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+                         BluetoothAudioProviderHearingAidSoftwareHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderLeAudioOutputSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+                         BluetoothAudioProviderLeAudioOutputSoftwareHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(
+    BluetoothAudioProviderLeAudioInputSoftwareHidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance,
+                         BluetoothAudioProviderLeAudioInputSoftwareHidlTest,
+                         testing::ValuesIn(kAudioInstances),
+                         android::hardware::PrintInstanceNameToString);
diff --git a/bluetooth/audio/utils/Android.bp b/bluetooth/audio/utils/Android.bp
new file mode 100644
index 0000000..551bc50
--- /dev/null
+++ b/bluetooth/audio/utils/Android.bp
@@ -0,0 +1,32 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+    name: "libbluetooth_audio_session",
+    defaults: ["hidl_defaults"],
+    vendor: true,
+    srcs: [
+        "session/BluetoothAudioSession.cpp",
+        "session/BluetoothAudioSession_2_1.cpp",
+        "session/BluetoothAudioSupportedCodecsDB.cpp",
+        "session/BluetoothAudioSupportedCodecsDB_2_1.cpp",
+    ],
+    export_include_dirs: ["session/"],
+    header_libs: ["libhardware_headers"],
+    shared_libs: [
+        "android.hardware.bluetooth.audio@2.0",
+        "android.hardware.bluetooth.audio@2.1",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "liblog",
+        "libutils",
+    ],
+}
diff --git a/bluetooth/audio/utils/OWNERS b/bluetooth/audio/utils/OWNERS
new file mode 100644
index 0000000..a35dde2
--- /dev/null
+++ b/bluetooth/audio/utils/OWNERS
@@ -0,0 +1,3 @@
+include platform/system/bt:/OWNERS
+
+cheneyni@google.com
\ No newline at end of file
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
new file mode 100644
index 0000000..2f3ddaf
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.cpp
@@ -0,0 +1,465 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#define LOG_TAG "BTAudioProviderSession"
+
+#include "BluetoothAudioSession.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::audio::common::V5_0::AudioContentType;
+using ::android::hardware::audio::common::V5_0::AudioUsage;
+using ::android::hardware::audio::common::V5_0::PlaybackTrackMetadata;
+using ::android::hardware::audio::common::V5_0::SourceMetadata;
+using ::android::hardware::bluetooth::audio::V2_0::CodecType;
+using ::android::hardware::bluetooth::audio::V2_0::TimeSpec;
+
+const CodecConfiguration BluetoothAudioSession::kInvalidCodecConfiguration = {
+    .codecType = CodecType::UNKNOWN,
+    .encodedAudioBitrate = 0x00000000,
+    .peerMtu = 0xffff,
+    .isScmstEnabled = false,
+    .config = {}};
+AudioConfiguration BluetoothAudioSession::invalidSoftwareAudioConfiguration =
+    {};
+AudioConfiguration BluetoothAudioSession::invalidOffloadAudioConfiguration = {};
+
+static constexpr int kFmqSendTimeoutMs = 1000;  // 1000 ms timeout for sending
+static constexpr int kFmqReceiveTimeoutMs =
+    1000;                                       // 1000 ms timeout for receiving
+static constexpr int kWritePollMs = 1;          // polled non-blocking interval
+static constexpr int kReadPollMs = 1;           // polled non-blocking interval
+
+static inline timespec timespec_convert_from_hal(const TimeSpec& TS) {
+  return {.tv_sec = static_cast<long>(TS.tvSec),
+          .tv_nsec = static_cast<long>(TS.tvNSec)};
+}
+
+BluetoothAudioSession::BluetoothAudioSession(const SessionType& session_type)
+    : session_type_(session_type), stack_iface_(nullptr), mDataMQ(nullptr) {
+  invalidSoftwareAudioConfiguration.pcmConfig(kInvalidPcmParameters);
+  invalidOffloadAudioConfiguration.codecConfig(kInvalidCodecConfiguration);
+}
+
+// The report function is used to report that the Bluetooth stack has started
+// this session without any failure, and will invoke session_changed_cb_ to
+// notify those registered bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionStarted(
+    const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+    const AudioConfiguration& audio_config) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (stack_iface == nullptr) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << ", IBluetoothAudioPort Invalid";
+  } else if (!UpdateAudioConfig(audio_config)) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << ", AudioConfiguration=" << toString(audio_config)
+               << " Invalid";
+  } else if (!UpdateDataPath(dataMQ)) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << " DataMQ Invalid";
+    audio_config_ =
+        (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+             ? kInvalidOffloadAudioConfiguration
+             : kInvalidSoftwareAudioConfiguration);
+  } else {
+    stack_iface_ = stack_iface;
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << ", AudioConfiguration=" << toString(audio_config);
+    ReportSessionStatus();
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has ended the
+// session, and will invoke session_changed_cb_ to notify registered
+// bluetooth_audio outputs
+void BluetoothAudioSession::OnSessionEnded() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  bool toggled = IsSessionReady();
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_);
+  audio_config_ = (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH
+                       ? kInvalidOffloadAudioConfiguration
+                       : kInvalidSoftwareAudioConfiguration);
+  stack_iface_ = nullptr;
+  UpdateDataPath(nullptr);
+  if (toggled) {
+    ReportSessionStatus();
+  }
+}
+
+// invoking the registered session_changed_cb_
+void BluetoothAudioSession::ReportSessionStatus() {
+  // This is locked already by OnSessionStarted / OnSessionEnded
+  if (observers_.empty()) {
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+    LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_)
+              << " notify to bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie);
+    cb->session_changed_cb_(cookie);
+  }
+}
+
+// The report function is used to report that the Bluetooth stack has notified
+// the result of startStream or suspendStream, and will invoke
+// control_result_cb_ to notify registered bluetooth_audio outputs
+void BluetoothAudioSession::ReportControlStatus(
+    bool start_resp, const BluetoothAudioStatus& status) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (observers_.empty()) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << " has NO port state observer";
+    return;
+  }
+  for (auto& observer : observers_) {
+    uint16_t cookie = observer.first;
+    std::shared_ptr<struct PortStatusCallbacks> cb = observer.second;
+    LOG(INFO) << __func__ << " - status=" << toString(status)
+              << " for SessionType=" << toString(session_type_)
+              << ", bluetooth_audio=0x"
+              << android::base::StringPrintf("%04x", cookie)
+              << (start_resp ? " started" : " suspended");
+    cb->control_result_cb_(cookie, start_resp, status);
+  }
+}
+
+// The function helps to check if this session is ready or not
+// @return: true if the Bluetooth stack has started the specified session
+bool BluetoothAudioSession::IsSessionReady() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  bool dataMQ_valid =
+      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+       (mDataMQ != nullptr && mDataMQ->isValid()));
+  return stack_iface_ != nullptr && dataMQ_valid;
+}
+
+bool BluetoothAudioSession::UpdateDataPath(const DataMQ::Descriptor* dataMQ) {
+  if (dataMQ == nullptr) {
+    // usecase of reset by nullptr
+    mDataMQ = nullptr;
+    return true;
+  }
+  std::unique_ptr<DataMQ> tempDataMQ;
+  tempDataMQ.reset(new DataMQ(*dataMQ));
+  if (!tempDataMQ || !tempDataMQ->isValid()) {
+    mDataMQ = nullptr;
+    return false;
+  }
+  mDataMQ = std::move(tempDataMQ);
+  return true;
+}
+
+bool BluetoothAudioSession::UpdateAudioConfig(
+    const AudioConfiguration& audio_config) {
+  bool is_software_session =
+      (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+       session_type_ == SessionType::HEARING_AID_SOFTWARE_ENCODING_DATAPATH);
+  bool is_offload_session =
+      (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  auto audio_config_discriminator = audio_config.getDiscriminator();
+  bool is_software_audio_config =
+      (is_software_session &&
+       audio_config_discriminator ==
+           AudioConfiguration::hidl_discriminator::pcmConfig);
+  bool is_offload_audio_config =
+      (is_offload_session &&
+       audio_config_discriminator ==
+           AudioConfiguration::hidl_discriminator::codecConfig);
+  if (!is_software_audio_config && !is_offload_audio_config) {
+    return false;
+  }
+  audio_config_ = audio_config;
+  return true;
+}
+
+// The control function helps the bluetooth_audio module to register
+// PortStatusCallbacks
+// @return: cookie - the assigned number to this bluetooth_audio output
+uint16_t BluetoothAudioSession::RegisterStatusCback(
+    const PortStatusCallbacks& cbacks) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  uint16_t cookie = ObserversCookieGetInitValue(session_type_);
+  uint16_t cookie_upper_bound = ObserversCookieGetUpperBound(session_type_);
+
+  while (cookie < cookie_upper_bound) {
+    if (observers_.find(cookie) == observers_.end()) {
+      break;
+    }
+    ++cookie;
+  }
+  if (cookie >= cookie_upper_bound) {
+    LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has " << observers_.size()
+               << " observers already (No Resource)";
+    return kObserversCookieUndefined;
+  }
+  std::shared_ptr<struct PortStatusCallbacks> cb =
+      std::make_shared<struct PortStatusCallbacks>();
+  *cb = cbacks;
+  observers_[cookie] = cb;
+  return cookie;
+}
+
+// The control function helps the bluetooth_audio module to unregister
+// PortStatusCallbacks
+// @param: cookie - indicates which bluetooth_audio output is
+void BluetoothAudioSession::UnregisterStatusCback(uint16_t cookie) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (observers_.erase(cookie) != 1) {
+    LOG(WARNING) << __func__ << " - SessionType=" << toString(session_type_)
+                 << " no such provider=0x"
+                 << android::base::StringPrintf("%04x", cookie);
+  }
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const AudioConfiguration& BluetoothAudioSession::GetAudioConfig() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (IsSessionReady()) {
+    return audio_config_;
+  } else if (session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return kInvalidOffloadAudioConfiguration;
+  } else {
+    return kInvalidSoftwareAudioConfiguration;
+  }
+}
+
+// Those control functions are for the bluetooth_audio module to start, suspend,
+// stop stream, to check position, and to update metadata.
+bool BluetoothAudioSession::StartStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  auto hal_retval = stack_iface_->startStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return true;
+}
+
+bool BluetoothAudioSession::SuspendStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  auto hal_retval = stack_iface_->suspendStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return true;
+}
+
+void BluetoothAudioSession::StopStream() {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    return;
+  }
+  auto hal_retval = stack_iface_->stopStream();
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+  }
+}
+
+bool BluetoothAudioSession::GetPresentationPosition(
+    uint64_t* remote_delay_report_ns, uint64_t* total_bytes_readed,
+    timespec* data_position) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return false;
+  }
+  bool retval = false;
+  auto hal_retval = stack_iface_->getPresentationPosition(
+      [&retval, &remote_delay_report_ns, &total_bytes_readed, &data_position](
+          BluetoothAudioStatus status,
+          const uint64_t& remoteDeviceAudioDelayNanos,
+          uint64_t transmittedOctets,
+          const TimeSpec& transmittedOctetsTimeStamp) {
+        if (status == BluetoothAudioStatus::SUCCESS) {
+          if (remote_delay_report_ns)
+            *remote_delay_report_ns = remoteDeviceAudioDelayNanos;
+          if (total_bytes_readed) *total_bytes_readed = transmittedOctets;
+          if (data_position)
+            *data_position =
+                timespec_convert_from_hal(transmittedOctetsTimeStamp);
+          retval = true;
+        }
+      });
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+    return false;
+  }
+  return retval;
+}
+
+void BluetoothAudioSession::UpdateTracksMetadata(
+    const struct source_metadata* source_metadata) {
+  std::lock_guard<std::recursive_mutex> guard(mutex_);
+  if (!IsSessionReady()) {
+    LOG(DEBUG) << __func__ << " - SessionType=" << toString(session_type_)
+               << " has NO session";
+    return;
+  }
+
+  ssize_t track_count = source_metadata->track_count;
+  LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_) << ", "
+            << track_count << " track(s)";
+  if (session_type_ == SessionType::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type_ == SessionType::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return;
+  }
+
+  struct playback_track_metadata* track = source_metadata->tracks;
+  SourceMetadata sourceMetadata;
+  PlaybackTrackMetadata* halMetadata;
+
+  sourceMetadata.tracks.resize(track_count);
+  halMetadata = sourceMetadata.tracks.data();
+  while (track_count && track) {
+    halMetadata->usage = static_cast<AudioUsage>(track->usage);
+    halMetadata->contentType =
+        static_cast<AudioContentType>(track->content_type);
+    halMetadata->gain = track->gain;
+    LOG(VERBOSE) << __func__ << " - SessionType=" << toString(session_type_)
+                 << ", usage=" << toString(halMetadata->usage)
+                 << ", content=" << toString(halMetadata->contentType)
+                 << ", gain=" << halMetadata->gain;
+    --track_count;
+    ++track;
+    ++halMetadata;
+  }
+  auto hal_retval = stack_iface_->updateMetadata(sourceMetadata);
+  if (!hal_retval.isOk()) {
+    LOG(WARNING) << __func__ << " - IBluetoothAudioPort SessionType="
+                 << toString(session_type_) << " failed";
+  }
+}
+
+// The control function writes stream to FMQ
+size_t BluetoothAudioSession::OutWritePcmData(const void* buffer,
+                                              size_t bytes) {
+  if (buffer == nullptr || !bytes) return 0;
+  size_t totalWritten = 0;
+  int ms_timeout = kFmqSendTimeoutMs;
+  do {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    if (!IsSessionReady()) break;
+    size_t availableToWrite = mDataMQ->availableToWrite();
+    if (availableToWrite) {
+      if (availableToWrite > (bytes - totalWritten)) {
+        availableToWrite = bytes - totalWritten;
+      }
+
+      if (!mDataMQ->write(static_cast<const uint8_t*>(buffer) + totalWritten,
+                          availableToWrite)) {
+        ALOGE("FMQ datapath writting %zu/%zu failed", totalWritten, bytes);
+        return totalWritten;
+      }
+      totalWritten += availableToWrite;
+    } else if (ms_timeout >= kWritePollMs) {
+      lock.unlock();
+      usleep(kWritePollMs * 1000);
+      ms_timeout -= kWritePollMs;
+    } else {
+      ALOGD("data %zu/%zu overflow %d ms", totalWritten, bytes,
+            (kFmqSendTimeoutMs - ms_timeout));
+      return totalWritten;
+    }
+  } while (totalWritten < bytes);
+  return totalWritten;
+}
+
+// The control function reads stream from FMQ
+size_t BluetoothAudioSession::InReadPcmData(void* buffer, size_t bytes) {
+  if (buffer == nullptr || !bytes) return 0;
+  size_t totalRead = 0;
+  int ms_timeout = kFmqReceiveTimeoutMs;
+  do {
+    std::unique_lock<std::recursive_mutex> lock(mutex_);
+    if (!IsSessionReady()) break;
+    size_t availableToRead = mDataMQ->availableToRead();
+    if (availableToRead) {
+      if (availableToRead > (bytes - totalRead)) {
+        availableToRead = bytes - totalRead;
+      }
+      if (!mDataMQ->read(static_cast<uint8_t*>(buffer) + totalRead,
+                         availableToRead)) {
+        ALOGE("FMQ datapath reading %zu/%zu failed", totalRead, bytes);
+        return totalRead;
+      }
+      totalRead += availableToRead;
+    } else if (ms_timeout >= kReadPollMs) {
+      lock.unlock();
+      usleep(kReadPollMs * 1000);
+      ms_timeout -= kReadPollMs;
+      continue;
+    } else {
+      ALOGD("in data %zu/%zu overflow %d ms", totalRead, bytes,
+            (kFmqReceiveTimeoutMs - ms_timeout));
+      return totalRead;
+    }
+  } while (totalRead < bytes);
+  return totalRead;
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance>
+    BluetoothAudioSessionInstance::instance_ptr =
+        std::unique_ptr<BluetoothAudioSessionInstance>(
+            new BluetoothAudioSessionInstance());
+
+// API to fetch the session of A2DP / Hearing Aid
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSessionInstance::GetSessionInstance(
+    const SessionType& session_type) {
+  std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
+  if (!instance_ptr->sessions_map_.empty()) {
+    auto entry = instance_ptr->sessions_map_.find(session_type);
+    if (entry != instance_ptr->sessions_map_.end()) {
+      return entry->second;
+    }
+  }
+  std::shared_ptr<BluetoothAudioSession> session_ptr =
+      std::make_shared<BluetoothAudioSession>(session_type);
+  instance_ptr->sessions_map_[session_type] = session_ptr;
+  return session_ptr;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession.h b/bluetooth/audio/utils/session/BluetoothAudioSession.h
new file mode 100644
index 0000000..83e20ad
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession.h
@@ -0,0 +1,190 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <mutex>
+#include <unordered_map>
+
+#include <android/hardware/bluetooth/audio/2.0/IBluetoothAudioPort.h>
+#include <fmq/MessageQueue.h>
+#include <hardware/audio.h>
+#include <hidl/MQDescriptor.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::sp;
+using ::android::hardware::kSynchronizedReadWrite;
+using ::android::hardware::MessageQueue;
+using ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+using ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration;
+using ::android::hardware::bluetooth::audio::V2_0::IBluetoothAudioPort;
+using ::android::hardware::bluetooth::audio::V2_0::PcmParameters;
+using ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+using BluetoothAudioStatus =
+    ::android::hardware::bluetooth::audio::V2_0::Status;
+
+using DataMQ = MessageQueue<uint8_t, kSynchronizedReadWrite>;
+
+static constexpr uint16_t kObserversCookieSize = 0x0010;  // 0x0000 ~ 0x000f
+constexpr uint16_t kObserversCookieUndefined =
+    (static_cast<uint16_t>(SessionType::UNKNOWN) << 8 & 0xff00);
+inline SessionType ObserversCookieGetSessionType(uint16_t cookie) {
+  return static_cast<SessionType>(cookie >> 8 & 0x00ff);
+}
+inline uint16_t ObserversCookieGetInitValue(SessionType session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00);
+}
+inline uint16_t ObserversCookieGetUpperBound(SessionType session_type) {
+  return (static_cast<uint16_t>(session_type) << 8 & 0xff00) +
+         kObserversCookieSize;
+}
+
+// This presents the callbacks of started / suspended and session changed,
+// and the bluetooth_audio module uses to receive the status notification
+struct PortStatusCallbacks {
+  // control_result_cb_ - when the Bluetooth stack reports results of
+  // streamStarted or streamSuspended, the BluetoothAudioProvider will invoke
+  // this callback to report to the bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  // @param: start_resp - this report is for startStream or not
+  // @param: status - the result of startStream
+  std::function<void(uint16_t cookie, bool start_resp,
+                     const BluetoothAudioStatus& status)>
+      control_result_cb_;
+  // session_changed_cb_ - when the Bluetooth stack start / end session, the
+  // BluetoothAudioProvider will invoke this callback to notify to the
+  // bluetooth_audio module.
+  // @param: cookie - indicates which bluetooth_audio output should handle
+  std::function<void(uint16_t cookie)> session_changed_cb_;
+};
+
+class BluetoothAudioSession {
+  friend class BluetoothAudioSession_2_1;
+
+ private:
+  // using recursive_mutex to allow hwbinder to re-enter agian.
+  std::recursive_mutex mutex_;
+  SessionType session_type_;
+
+  // audio control path to use for both software and offloading
+  sp<IBluetoothAudioPort> stack_iface_;
+  // audio data path (FMQ) for software encoding
+  std::unique_ptr<DataMQ> mDataMQ;
+  // audio data configuration for both software and offloading
+  AudioConfiguration audio_config_;
+
+  static AudioConfiguration invalidSoftwareAudioConfiguration;
+  static AudioConfiguration invalidOffloadAudioConfiguration;
+
+  // saving those registered bluetooth_audio's callbacks
+  std::unordered_map<uint16_t, std::shared_ptr<struct PortStatusCallbacks>>
+      observers_;
+
+  bool UpdateDataPath(const DataMQ::Descriptor* dataMQ);
+  bool UpdateAudioConfig(const AudioConfiguration& audio_config);
+  // invoking the registered session_changed_cb_
+  void ReportSessionStatus();
+
+ public:
+  BluetoothAudioSession(const SessionType& session_type);
+
+  // The function helps to check if this session is ready or not
+  // @return: true if the Bluetooth stack has started the specified session
+  bool IsSessionReady();
+
+  // The report function is used to report that the Bluetooth stack has started
+  // this session without any failure, and will invoke session_changed_cb_ to
+  // notify those registered bluetooth_audio outputs
+  void OnSessionStarted(const sp<IBluetoothAudioPort> stack_iface,
+                        const DataMQ::Descriptor* dataMQ,
+                        const AudioConfiguration& audio_config);
+
+  // The report function is used to report that the Bluetooth stack has ended
+  // the session, and will invoke session_changed_cb_ to notify registered
+  // bluetooth_audio outputs
+  void OnSessionEnded();
+
+  // The report function is used to report that the Bluetooth stack has notified
+  // the result of startStream or suspendStream, and will invoke
+  // control_result_cb_ to notify registered bluetooth_audio outputs
+  void ReportControlStatus(bool start_resp, const BluetoothAudioStatus& status);
+
+  // The control function helps the bluetooth_audio module to register
+  // PortStatusCallbacks
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  uint16_t RegisterStatusCback(const PortStatusCallbacks& cbacks);
+
+  // The control function helps the bluetooth_audio module to unregister
+  // PortStatusCallbacks
+  // @param: cookie - indicates which bluetooth_audio output is
+  void UnregisterStatusCback(uint16_t cookie);
+
+  // The control function is for the bluetooth_audio module to get the current
+  // AudioConfiguration
+  const AudioConfiguration& GetAudioConfig();
+
+  // Those control functions are for the bluetooth_audio module to start,
+  // suspend, stop stream, to check position, and to update metadata.
+  bool StartStream();
+  bool SuspendStream();
+  void StopStream();
+  bool GetPresentationPosition(uint64_t* remote_delay_report_ns,
+                               uint64_t* total_bytes_readed,
+                               timespec* data_position);
+  void UpdateTracksMetadata(const struct source_metadata* source_metadata);
+
+  // The control function writes stream to FMQ
+  size_t OutWritePcmData(const void* buffer, size_t bytes);
+  // The control function read stream from FMQ
+  size_t InReadPcmData(void* buffer, size_t bytes);
+
+  static constexpr PcmParameters kInvalidPcmParameters = {
+      .sampleRate = SampleRate::RATE_UNKNOWN,
+      .channelMode = ChannelMode::UNKNOWN,
+      .bitsPerSample = BitsPerSample::BITS_UNKNOWN,
+  };
+  // can't be constexpr because of non-literal type
+  static const CodecConfiguration kInvalidCodecConfiguration;
+
+  static constexpr AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+      invalidSoftwareAudioConfiguration;
+  static constexpr AudioConfiguration& kInvalidOffloadAudioConfiguration =
+      invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance {
+ public:
+  // The API is to fetch the specified session of A2DP / Hearing Aid
+  static std::shared_ptr<BluetoothAudioSession> GetSessionInstance(
+      const SessionType& session_type);
+
+ private:
+  static std::unique_ptr<BluetoothAudioSessionInstance> instance_ptr;
+  std::mutex mutex_;
+  std::unordered_map<SessionType, std::shared_ptr<BluetoothAudioSession>>
+      sessions_map_;
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl.h
similarity index 100%
rename from bluetooth/audio/2.0/default/session/BluetoothAudioSessionControl.h
rename to bluetooth/audio/utils/session/BluetoothAudioSessionControl.h
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
new file mode 100644
index 0000000..95f7408
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionControl_2_1.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession_2_1.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionControl_2_1 {
+  using SessionType_2_1 =
+      ::android::hardware::bluetooth::audio::V2_1::SessionType;
+  using AudioConfiguration_2_1 =
+      ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration;
+
+ public:
+  // The control API helps to check if session is ready or not
+  // @return: true if the Bluetooth stack has started th specified session
+  static bool IsSessionReady(const SessionType_2_1& session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->IsSessionReady();
+    }
+    return false;
+  }
+
+  // The control API helps the bluetooth_audio module to register
+  // PortStatusCallbacks
+  // @return: cookie - the assigned number to this bluetooth_audio output
+  static uint16_t RegisterControlResultCback(
+      const SessionType_2_1& session_type, const PortStatusCallbacks& cbacks) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->RegisterStatusCback(cbacks);
+    }
+    return kObserversCookieUndefined;
+  }
+
+  // The control API helps the bluetooth_audio module to unregister
+  // PortStatusCallbacks
+  // @param: cookie - indicates which bluetooth_audio output is
+  static void UnregisterControlResultCback(const SessionType_2_1& session_type,
+                                           uint16_t cookie) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->GetAudioSession()->UnregisterStatusCback(cookie);
+    }
+  }
+
+  // The control API for the bluetooth_audio module to get current
+  // AudioConfiguration
+  static const AudioConfiguration_2_1 GetAudioConfig(
+      const SessionType_2_1& session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioConfig();
+    } else if (session_type ==
+               SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+      return BluetoothAudioSession_2_1::kInvalidOffloadAudioConfiguration;
+    } else {
+      return BluetoothAudioSession_2_1::kInvalidSoftwareAudioConfiguration;
+    }
+  }
+
+  // Those control APIs for the bluetooth_audio module to start / suspend / stop
+  // stream, to check position, and to update metadata.
+  static bool StartStream(const SessionType_2_1& session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->StartStream();
+    }
+    return false;
+  }
+
+  static bool SuspendStream(const SessionType_2_1& session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->SuspendStream();
+    }
+    return false;
+  }
+
+  static void StopStream(const SessionType_2_1& session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->GetAudioSession()->StopStream();
+    }
+  }
+
+  static bool GetPresentationPosition(const SessionType_2_1& session_type,
+                                      uint64_t* remote_delay_report_ns,
+                                      uint64_t* total_bytes_readed,
+                                      timespec* data_position) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->GetPresentationPosition(
+          remote_delay_report_ns, total_bytes_readed, data_position);
+    }
+    return false;
+  }
+
+  static void UpdateTracksMetadata(
+      const SessionType_2_1& session_type,
+      const struct source_metadata* source_metadata) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->GetAudioSession()->UpdateTracksMetadata(source_metadata);
+    }
+  }
+
+  // The control API writes stream to FMQ
+  static size_t OutWritePcmData(const SessionType_2_1& session_type,
+                                const void* buffer, size_t bytes) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->OutWritePcmData(buffer, bytes);
+    }
+    return 0;
+  }
+
+  // The control API reads stream from FMQ
+  static size_t InReadPcmData(const SessionType_2_1& session_type, void* buffer,
+                              size_t bytes) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      return session_ptr->GetAudioSession()->InReadPcmData(buffer, bytes);
+    }
+    return 0;
+  }
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport.h
similarity index 100%
rename from bluetooth/audio/2.0/default/session/BluetoothAudioSessionReport.h
rename to bluetooth/audio/utils/session/BluetoothAudioSessionReport.h
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_1.h b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_1.h
new file mode 100644
index 0000000..ab30536
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSessionReport_2_1.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSession_2_1.h"
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSessionReport_2_1 {
+ public:
+  // The API reports the Bluetooth stack has started the session, and will
+  // inform registered bluetooth_audio outputs
+  static void OnSessionStarted(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type,
+      const sp<IBluetoothAudioPort> host_iface,
+      const DataMQ::Descriptor* dataMQ,
+      const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
+          audio_config) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->OnSessionStarted(host_iface, dataMQ, audio_config);
+    }
+  }
+  // The API reports the Bluetooth stack has ended the session, and will
+  // inform registered bluetooth_audio outputs
+  static void OnSessionEnded(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->GetAudioSession()->OnSessionEnded();
+    }
+  }
+  // The API reports the Bluetooth stack has replied the result of startStream
+  // or suspendStream, and will inform registered bluetooth_audio outputs
+  static void ReportControlStatus(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type,
+      const bool& start_resp, const BluetoothAudioStatus& status) {
+    std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+        BluetoothAudioSessionInstance_2_1::GetSessionInstance(session_type);
+    if (session_ptr != nullptr) {
+      session_ptr->GetAudioSession()->ReportControlStatus(start_resp, status);
+    }
+  }
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
new file mode 100644
index 0000000..c250ef1
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#define LOG_TAG "BTAudioProviderSession_2_1"
+
+#include "BluetoothAudioSession_2_1.h"
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+using SessionType_2_1 =
+    ::android::hardware::bluetooth::audio::V2_1::SessionType;
+using SessionType_2_0 =
+    ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+    BluetoothAudioSession_2_1::invalidSoftwareAudioConfiguration = {};
+::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+    BluetoothAudioSession_2_1::invalidOffloadAudioConfiguration = {};
+
+namespace {
+bool is_2_0_session_type(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type) {
+  if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+      session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
+    return true;
+  } else {
+    return false;
+  }
+}
+}  // namespace
+
+BluetoothAudioSession_2_1::BluetoothAudioSession_2_1(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type)
+    : audio_session(BluetoothAudioSessionInstance::GetSessionInstance(
+          static_cast<SessionType_2_0>(session_type))) {
+  if (is_2_0_session_type(session_type)) {
+    session_type_2_1_ = (SessionType_2_1::UNKNOWN);
+  } else {
+    session_type_2_1_ = (session_type);
+  }
+}
+
+bool BluetoothAudioSession_2_1::IsSessionReady() {
+  if (session_type_2_1_ !=
+      SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH) {
+    return audio_session->IsSessionReady();
+  }
+
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  return audio_session->stack_iface_ != nullptr;
+}
+
+std::shared_ptr<BluetoothAudioSession>
+BluetoothAudioSession_2_1::GetAudioSession() {
+  return audio_session;
+}
+
+// The control function is for the bluetooth_audio module to get the current
+// AudioConfiguration
+const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+BluetoothAudioSession_2_1::GetAudioConfig() {
+  std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+  if (IsSessionReady()) {
+    // If session is unknown it means it should be 2.0 type
+    if (session_type_2_1_ != SessionType_2_1::UNKNOWN)
+      return audio_config_2_1_;
+
+    ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration toConf;
+    const AudioConfiguration fromConf = GetAudioSession()->GetAudioConfig();
+    // pcmConfig only differs between 2.0 and 2.1 in AudioConfiguration
+    if (fromConf.getDiscriminator() ==
+        AudioConfiguration::hidl_discriminator::codecConfig) {
+      toConf.codecConfig() = fromConf.codecConfig();
+    } else {
+      toConf.pcmConfig() = {
+          .sampleRate = static_cast<
+              ::android::hardware::bluetooth::audio::V2_1::SampleRate>(
+              fromConf.pcmConfig().sampleRate),
+          .channelMode = fromConf.pcmConfig().channelMode,
+          .bitsPerSample = fromConf.pcmConfig().bitsPerSample,
+          .dataIntervalUs = 0};
+    }
+    return toConf;
+  } else if (session_type_2_1_ ==
+             SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH) {
+    return kInvalidOffloadAudioConfiguration;
+  } else {
+    return kInvalidSoftwareAudioConfiguration;
+  }
+}
+
+bool BluetoothAudioSession_2_1::UpdateAudioConfig(
+    const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
+        audio_config) {
+  bool is_software_session =
+      (session_type_2_1_ == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+       session_type_2_1_ ==
+           SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH ||
+       session_type_2_1_ ==
+           SessionType_2_1::LE_AUDIO_SOFTWARE_ENCODING_DATAPATH ||
+       session_type_2_1_ ==
+           SessionType_2_1::LE_AUDIO_SOFTWARE_DECODED_DATAPATH);
+  bool is_offload_a2dp_session =
+      (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH);
+  bool is_offload_le_audio_session =
+      (session_type_2_1_ == SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH ||
+       session_type_2_1_ == SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH);
+  auto audio_config_discriminator = audio_config.getDiscriminator();
+  bool is_software_audio_config =
+      (is_software_session &&
+       audio_config_discriminator ==
+           ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+               hidl_discriminator::pcmConfig);
+  bool is_a2dp_offload_audio_config =
+      (is_offload_a2dp_session &&
+       audio_config_discriminator ==
+           ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+               hidl_discriminator::codecConfig);
+  bool is_le_audio_offload_audio_config =
+      (is_offload_le_audio_session &&
+       audio_config_discriminator ==
+           ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+               hidl_discriminator::leAudioCodecConfig);
+  if (!is_software_audio_config && !is_a2dp_offload_audio_config &&
+      !is_le_audio_offload_audio_config) {
+    return false;
+  }
+  audio_config_2_1_ = audio_config;
+  return true;
+}
+
+// The report function is used to report that the Bluetooth stack has started
+// this session without any failure, and will invoke session_changed_cb_ to
+// notify those registered bluetooth_audio outputs
+void BluetoothAudioSession_2_1::OnSessionStarted(
+    const sp<IBluetoothAudioPort> stack_iface, const DataMQ::Descriptor* dataMQ,
+    const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
+        audio_config) {
+  if (session_type_2_1_ == SessionType_2_1::UNKNOWN) {
+    ::android::hardware::bluetooth::audio::V2_0::AudioConfiguration config;
+    if (audio_config.getDiscriminator() ==
+        ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration::
+            hidl_discriminator::codecConfig) {
+      config.codecConfig(audio_config.codecConfig());
+    } else {
+      auto& tmpPcm = audio_config.pcmConfig();
+      config.pcmConfig(
+          ::android::hardware::bluetooth::audio::V2_0::PcmParameters{
+              .sampleRate = static_cast<SampleRate>(tmpPcm.sampleRate),
+              .channelMode = tmpPcm.channelMode,
+              .bitsPerSample = tmpPcm.bitsPerSample
+              /*dataIntervalUs is not passed to 2.0 */
+          });
+    }
+
+    audio_session->OnSessionStarted(stack_iface, dataMQ, config);
+  } else {
+    std::lock_guard<std::recursive_mutex> guard(audio_session->mutex_);
+    if (stack_iface == nullptr) {
+      LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << ", IBluetoothAudioPort Invalid";
+    } else if (!UpdateAudioConfig(audio_config)) {
+      LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << ", AudioConfiguration=" << toString(audio_config)
+                 << " Invalid";
+    } else if (!audio_session->UpdateDataPath(dataMQ)) {
+      LOG(ERROR) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                 << " DataMQ Invalid";
+      audio_config_2_1_ =
+          (session_type_2_1_ == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH
+               ? kInvalidOffloadAudioConfiguration
+               : kInvalidSoftwareAudioConfiguration);
+    } else {
+      audio_session->stack_iface_ = stack_iface;
+      LOG(INFO) << __func__ << " - SessionType=" << toString(session_type_2_1_)
+                << ", AudioConfiguration=" << toString(audio_config);
+      audio_session->ReportSessionStatus();
+    };
+  }
+}
+
+std::unique_ptr<BluetoothAudioSessionInstance_2_1>
+    BluetoothAudioSessionInstance_2_1::instance_ptr =
+        std::unique_ptr<BluetoothAudioSessionInstance_2_1>(
+            new BluetoothAudioSessionInstance_2_1());
+
+// API to fetch the session of A2DP / Hearing Aid
+std::shared_ptr<BluetoothAudioSession_2_1>
+BluetoothAudioSessionInstance_2_1::GetSessionInstance(
+    const SessionType_2_1& session_type) {
+  std::lock_guard<std::mutex> guard(instance_ptr->mutex_);
+  if (!instance_ptr->sessions_map_.empty()) {
+    auto entry = instance_ptr->sessions_map_.find(session_type);
+    if (entry != instance_ptr->sessions_map_.end()) {
+      return entry->second;
+    }
+  }
+  std::shared_ptr<BluetoothAudioSession_2_1> session_ptr =
+      std::make_shared<BluetoothAudioSession_2_1>(session_type);
+  instance_ptr->sessions_map_[session_type] = session_ptr;
+  return session_ptr;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
new file mode 100644
index 0000000..db82c73
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSession_2_1.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#pragma once
+
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+#include "BluetoothAudioSession.h"
+
+#include <mutex>
+#include <unordered_map>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+class BluetoothAudioSession_2_1 {
+ private:
+  std::shared_ptr<BluetoothAudioSession> audio_session;
+
+  ::android::hardware::bluetooth::audio::V2_1::SessionType session_type_2_1_;
+
+  // audio data configuration for both software and offloading
+  ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+      audio_config_2_1_;
+
+  bool UpdateAudioConfig(
+      const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
+          audio_config);
+
+  static ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+      invalidSoftwareAudioConfiguration;
+  static ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+      invalidOffloadAudioConfiguration;
+
+ public:
+  BluetoothAudioSession_2_1(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type);
+
+  // The function helps to check if this session is ready or not
+  // @return: true if the Bluetooth stack has started the specified session
+  bool IsSessionReady();
+
+  std::shared_ptr<BluetoothAudioSession> GetAudioSession();
+
+  // The report function is used to report that the Bluetooth stack has started
+  // this session without any failure, and will invoke session_changed_cb_ to
+  // notify those registered bluetooth_audio outputs
+  void OnSessionStarted(
+      const sp<IBluetoothAudioPort> stack_iface,
+      const DataMQ::Descriptor* dataMQ,
+      const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration&
+          audio_config);
+
+  // The control function is for the bluetooth_audio module to get the current
+  // AudioConfiguration
+  const ::android::hardware::bluetooth::audio::V2_1::AudioConfiguration
+  GetAudioConfig();
+
+  static constexpr ::android::hardware::bluetooth::audio::V2_1::
+      AudioConfiguration& kInvalidSoftwareAudioConfiguration =
+          invalidSoftwareAudioConfiguration;
+  static constexpr ::android::hardware::bluetooth::audio::V2_1::
+      AudioConfiguration& kInvalidOffloadAudioConfiguration =
+          invalidOffloadAudioConfiguration;
+};
+
+class BluetoothAudioSessionInstance_2_1 {
+ public:
+  // The API is to fetch the specified session of A2DP / Hearing Aid
+  static std::shared_ptr<BluetoothAudioSession_2_1> GetSessionInstance(
+      const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+          session_type);
+
+ private:
+  static std::unique_ptr<BluetoothAudioSessionInstance_2_1> instance_ptr;
+  std::mutex mutex_;
+  std::unordered_map<::android::hardware::bluetooth::audio::V2_1::SessionType,
+                     std::shared_ptr<BluetoothAudioSession_2_1>>
+      sessions_map_;
+};
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB.cpp
similarity index 100%
rename from bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.cpp
rename to bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB.cpp
diff --git a/bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB.h
similarity index 100%
rename from bluetooth/audio/2.0/default/session/BluetoothAudioSupportedCodecsDB.h
rename to bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB.h
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
new file mode 100644
index 0000000..c90ce6d
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.cpp
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2018 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.
+ */
+
+#define LOG_TAG "BTAudioProviderSessionCodecsDB_2_1"
+
+#include "BluetoothAudioSupportedCodecsDB_2_1.h"
+
+#include <android-base/logging.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+using ::android::hardware::bluetooth::audio::V2_0::BitsPerSample;
+using ::android::hardware::bluetooth::audio::V2_0::ChannelMode;
+
+using SampleRate_2_0 = ::android::hardware::bluetooth::audio::V2_0::SampleRate;
+using SampleRate_2_1 = ::android::hardware::bluetooth::audio::V2_1::SampleRate;
+
+using SessionType_2_1 =
+    ::android::hardware::bluetooth::audio::V2_1::SessionType;
+using SessionType_2_0 =
+    ::android::hardware::bluetooth::audio::V2_0::SessionType;
+
+namespace {
+bool is_2_0_session_type(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type) {
+  if (session_type == SessionType_2_1::A2DP_SOFTWARE_ENCODING_DATAPATH ||
+      session_type == SessionType_2_1::A2DP_HARDWARE_OFFLOAD_DATAPATH ||
+      session_type == SessionType_2_1::HEARING_AID_SOFTWARE_ENCODING_DATAPATH) {
+    return true;
+  } else {
+    return false;
+  }
+}
+}  // namespace
+
+static const ::android::hardware::bluetooth::audio::V2_1::PcmParameters
+    kDefaultSoftwarePcmCapabilities_2_1 = {
+        .sampleRate = static_cast<SampleRate_2_1>(
+            SampleRate_2_1::RATE_44100 | SampleRate_2_1::RATE_48000 |
+            SampleRate_2_1::RATE_88200 | SampleRate_2_1::RATE_96000 |
+            SampleRate_2_1::RATE_16000 | SampleRate_2_1::RATE_24000),
+        .channelMode =
+            static_cast<ChannelMode>(ChannelMode::MONO | ChannelMode::STEREO),
+        .bitsPerSample = static_cast<BitsPerSample>(BitsPerSample::BITS_16 |
+                                                    BitsPerSample::BITS_24 |
+                                                    BitsPerSample::BITS_32)};
+
+std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
+GetSoftwarePcmCapabilities_2_1() {
+  return std::vector<
+      ::android::hardware::bluetooth::audio::V2_1::PcmParameters>(
+      1, kDefaultSoftwarePcmCapabilities_2_1);
+}
+
+std::vector<CodecCapabilities> GetOffloadCodecCapabilities(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type) {
+  if (is_2_0_session_type(session_type)) {
+    return GetOffloadCodecCapabilities(
+        static_cast<SessionType_2_0>(session_type));
+  }
+  return std::vector<CodecCapabilities>(0);
+}
+
+bool IsSoftwarePcmConfigurationValid_2_1(
+    const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
+        pcm_config) {
+  if ((pcm_config.sampleRate != SampleRate_2_1::RATE_44100 &&
+       pcm_config.sampleRate != SampleRate_2_1::RATE_48000 &&
+       pcm_config.sampleRate != SampleRate_2_1::RATE_88200 &&
+       pcm_config.sampleRate != SampleRate_2_1::RATE_96000 &&
+       pcm_config.sampleRate != SampleRate_2_1::RATE_16000 &&
+       pcm_config.sampleRate != SampleRate_2_1::RATE_24000) ||
+      (pcm_config.bitsPerSample != BitsPerSample::BITS_16 &&
+       pcm_config.bitsPerSample != BitsPerSample::BITS_24 &&
+       pcm_config.bitsPerSample != BitsPerSample::BITS_32) ||
+      (pcm_config.channelMode != ChannelMode::MONO &&
+       pcm_config.channelMode != ChannelMode::STEREO)) {
+    LOG(WARNING) << __func__
+                 << ": Invalid PCM Configuration=" << toString(pcm_config);
+    return false;
+  } else if (pcm_config.sampleRate &
+                 kDefaultSoftwarePcmCapabilities_2_1.sampleRate &&
+             pcm_config.bitsPerSample &
+                 kDefaultSoftwarePcmCapabilities_2_1.bitsPerSample &&
+             pcm_config.channelMode &
+                 kDefaultSoftwarePcmCapabilities_2_1.channelMode &&
+             pcm_config.dataIntervalUs != 0) {
+    return true;
+  }
+  LOG(WARNING) << __func__
+               << ": Unsupported PCM Configuration=" << toString(pcm_config);
+  return false;
+}
+
+bool IsOffloadCodecConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
+        codec_config) {
+  if (is_2_0_session_type(session_type)) {
+    return IsOffloadCodecConfigurationValid(
+        static_cast<SessionType_2_0>(session_type), codec_config);
+  }
+
+  return false;
+}
+
+bool IsOffloadLeAudioConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&) {
+
+  if (session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_ENCODING_DATAPATH &&
+      session_type != SessionType_2_1::LE_AUDIO_HARDWARE_OFFLOAD_DECODING_DATAPATH) {
+    return false;
+  }
+
+  //TODO: perform checks on le_audio_codec_config once we know supported parameters
+
+  return true;
+}
+
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h
new file mode 100644
index 0000000..a52636c
--- /dev/null
+++ b/bluetooth/audio/utils/session/BluetoothAudioSupportedCodecsDB_2_1.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include "BluetoothAudioSupportedCodecsDB.h"
+
+#include <android/hardware/bluetooth/audio/2.1/types.h>
+
+namespace android {
+namespace bluetooth {
+namespace audio {
+
+std::vector<::android::hardware::bluetooth::audio::V2_1::PcmParameters>
+GetSoftwarePcmCapabilities_2_1();
+std::vector<::android::hardware::bluetooth::audio::V2_0::CodecCapabilities>
+GetOffloadCodecCapabilities(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type);
+
+bool IsSoftwarePcmConfigurationValid_2_1(
+    const ::android::hardware::bluetooth::audio::V2_1::PcmParameters&
+        pcm_config);
+
+bool IsOffloadCodecConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_0::CodecConfiguration&
+        codec_config);
+
+bool IsOffloadLeAudioConfigurationValid(
+    const ::android::hardware::bluetooth::audio::V2_1::SessionType&
+        session_type,
+    const ::android::hardware::bluetooth::audio::V2_1::Lc3CodecConfiguration&
+        le_audio_codec_config);
+}  // namespace audio
+}  // namespace bluetooth
+}  // namespace android
diff --git a/boot/1.0/Android.bp b/boot/1.0/Android.bp
index 5568436..8117066 100644
--- a/boot/1.0/Android.bp
+++ b/boot/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.boot@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBootControl.hal",
diff --git a/boot/1.0/default/Android.bp b/boot/1.0/default/Android.bp
index fdf7a1e..b7d2ec8 100644
--- a/boot/1.0/default/Android.bp
+++ b/boot/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.boot@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/boot/1.0/vts/functional/Android.bp b/boot/1.0/vts/functional/Android.bp
index 92c818c..90ee9cb 100644
--- a/boot/1.0/vts/functional/Android.bp
+++ b/boot/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBootV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp b/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
index 613c528..e4ad1b9 100644
--- a/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
+++ b/boot/1.0/vts/functional/VtsHalBootV1_0TargetTest.cpp
@@ -80,7 +80,6 @@
     }
 }
 
-// Sanity check Boot::setActiveBootSlot() on good and bad inputs.
 TEST_P(BootHidlTest, SetActiveBootSlot) {
     Slot curSlot = boot->getCurrentSlot();
     Slot otherSlot = curSlot ? 0 : 1;
@@ -117,7 +116,6 @@
     }
 }
 
-// Sanity check Boot::setSlotAsUnbootable() on good and bad inputs.
 TEST_P(BootHidlTest, SetSlotAsUnbootable) {
     Slot curSlot = boot->getCurrentSlot();
     Slot otherSlot = curSlot ? 0 : 1;
@@ -193,6 +191,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, BootHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
diff --git a/boot/1.1/Android.bp b/boot/1.1/Android.bp
index 6a8d57a..a2a8447 100644
--- a/boot/1.1/Android.bp
+++ b/boot/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.boot@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBootControl.hal",
diff --git a/boot/1.1/default/Android.bp b/boot/1.1/default/Android.bp
index abf1bf9..0b0a5b7 100644
--- a/boot/1.1/default/Android.bp
+++ b/boot/1.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.boot@1.1-impl",
     stem: "android.hardware.boot@1.0-impl-1.1",
diff --git a/boot/1.1/default/boot_control/Android.bp b/boot/1.1/default/boot_control/Android.bp
index b2e68df..ad71700 100644
--- a/boot/1.1/default/boot_control/Android.bp
+++ b/boot/1.1/default/boot_control/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "libboot_control_defaults",
     vendor: true,
diff --git a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
index 5468658..ac17d6d 100644
--- a/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
+++ b/boot/1.1/default/boot_control/include/libboot_control/libboot_control.h
@@ -32,6 +32,7 @@
   unsigned int GetNumberSlots();
   unsigned int GetCurrentSlot();
   bool MarkBootSuccessful();
+  unsigned int GetActiveBootSlot();
   bool SetActiveBootSlot(unsigned int slot);
   bool SetSlotAsUnbootable(unsigned int slot);
   bool SetSlotBootable(unsigned int slot);
diff --git a/boot/1.1/default/boot_control/libboot_control.cpp b/boot/1.1/default/boot_control/libboot_control.cpp
index 2c6ccaf..9387c32 100644
--- a/boot/1.1/default/boot_control/libboot_control.cpp
+++ b/boot/1.1/default/boot_control/libboot_control.cpp
@@ -261,6 +261,24 @@
   return UpdateAndSaveBootloaderControl(misc_device_, &bootctrl);
 }
 
+unsigned int BootControl::GetActiveBootSlot() {
+  bootloader_control bootctrl;
+  if (!LoadBootloaderControl(misc_device_, &bootctrl)) return false;
+
+  // Use the current slot by default.
+  unsigned int active_boot_slot = current_slot_;
+  unsigned int max_priority = bootctrl.slot_info[current_slot_].priority;
+  // Find the slot with the highest priority.
+  for (unsigned int i = 0; i < num_slots_; ++i) {
+    if (bootctrl.slot_info[i].priority > max_priority) {
+      max_priority = bootctrl.slot_info[i].priority;
+      active_boot_slot = i;
+    }
+  }
+
+  return active_boot_slot;
+}
+
 bool BootControl::SetActiveBootSlot(unsigned int slot) {
   if (slot >= kMaxNumSlots || slot >= num_slots_) {
     // Invalid slot number.
diff --git a/boot/1.1/vts/functional/Android.bp b/boot/1.1/vts/functional/Android.bp
index 9f0c74a..3a36046 100644
--- a/boot/1.1/vts/functional/Android.bp
+++ b/boot/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBootV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
index 30b965d..c38f257 100644
--- a/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
+++ b/boot/1.1/vts/functional/VtsHalBootV1_1TargetTest.cpp
@@ -84,6 +84,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, BootHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
diff --git a/boot/1.2/Android.bp b/boot/1.2/Android.bp
new file mode 100644
index 0000000..17ba3e9
--- /dev/null
+++ b/boot/1.2/Android.bp
@@ -0,0 +1,24 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.boot@1.2",
+    root: "android.hardware",
+    srcs: [
+        "IBootControl.hal",
+    ],
+    interfaces: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/boot/1.2/IBootControl.hal b/boot/1.2/IBootControl.hal
new file mode 100644
index 0000000..bb0ad13
--- /dev/null
+++ b/boot/1.2/IBootControl.hal
@@ -0,0 +1,35 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.boot@1.2;
+
+import @1.0::IBootControl;
+import @1.0::Slot;
+import @1.1::IBootControl;
+
+interface IBootControl extends @1.1::IBootControl {
+
+  /**
+   * Returns the active slot to boot into on the next boot. If
+   * setActiveBootSlot() has been called, the getter function should return the
+   * same slot as the one provided in the last setActiveBootSlot() call.
+   * The returned value is always guaranteed to be strictly less than the
+   * value returned by getNumberSlots. Slots start at 0 and finish at
+   * getNumberSlots() - 1. For instance, a system with A/B must return 0 or 1.
+   */
+   getActiveBootSlot() generates (Slot slot);
+};
+
diff --git a/boot/1.2/default/Android.bp b/boot/1.2/default/Android.bp
new file mode 100644
index 0000000..4e1c35e
--- /dev/null
+++ b/boot/1.2/default/Android.bp
@@ -0,0 +1,59 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_shared {
+    name: "android.hardware.boot@1.2-impl",
+    stem: "android.hardware.boot@1.0-impl-1.2",
+    defaults: [
+        "hidl_defaults",
+        "libboot_control_defaults",
+    ],
+    relative_install_path: "hw",
+    vendor: true,
+    recovery_available: true,
+    srcs: ["BootControl.cpp"],
+
+    shared_libs: [
+        "liblog",
+        "libhidlbase",
+        "libhardware",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+    static_libs: [
+        "libboot_control",
+        "libfstab",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.boot@1.2-service",
+    defaults: ["hidl_defaults"],
+    relative_install_path: "hw",
+    vendor: true,
+    init_rc: ["android.hardware.boot@1.2-service.rc"],
+    srcs: ["service.cpp"],
+
+    vintf_fragments: [
+        "android.hardware.boot@1.2.xml",
+    ],
+
+    shared_libs: [
+        "liblog",
+        "libhardware",
+        "libhidlbase",
+        "libutils",
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+    ],
+
+}
diff --git a/boot/1.2/default/BootControl.cpp b/boot/1.2/default/BootControl.cpp
new file mode 100644
index 0000000..c0bf02f
--- /dev/null
+++ b/boot/1.2/default/BootControl.cpp
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "android.hardware.boot@1.2-impl"
+
+#include <memory>
+
+#include <log/log.h>
+
+#include "BootControl.h"
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+
+using ::android::hardware::boot::V1_0::CommandResult;
+
+bool BootControl::Init() {
+    return impl_.Init();
+}
+
+// Methods from ::android::hardware::boot::V1_0::IBootControl.
+Return<uint32_t> BootControl::getNumberSlots() {
+    return impl_.GetNumberSlots();
+}
+
+Return<uint32_t> BootControl::getCurrentSlot() {
+    return impl_.GetCurrentSlot();
+}
+
+Return<void> BootControl::markBootSuccessful(markBootSuccessful_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.MarkBootSuccessful()) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetActiveBootSlot(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<void> BootControl::setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) {
+    struct CommandResult cr;
+    if (impl_.SetSlotAsUnbootable(slot)) {
+        cr.success = true;
+        cr.errMsg = "Success";
+    } else {
+        cr.success = false;
+        cr.errMsg = "Operation failed";
+    }
+    _hidl_cb(cr);
+    return Void();
+}
+
+Return<BoolResult> BootControl::isSlotBootable(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotBootable(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<BoolResult> BootControl::isSlotMarkedSuccessful(uint32_t slot) {
+    if (!impl_.IsValidSlot(slot)) {
+        return BoolResult::INVALID_SLOT;
+    }
+    return impl_.IsSlotMarkedSuccessful(slot) ? BoolResult::TRUE : BoolResult::FALSE;
+}
+
+Return<void> BootControl::getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) {
+    hidl_string ans;
+    const char* suffix = impl_.GetSuffix(slot);
+    if (suffix) {
+        ans = suffix;
+    }
+    _hidl_cb(ans);
+    return Void();
+}
+
+// Methods from ::android::hardware::boot::V1_1::IBootControl.
+Return<bool> BootControl::setSnapshotMergeStatus(MergeStatus status) {
+    return impl_.SetSnapshotMergeStatus(status);
+}
+
+Return<MergeStatus> BootControl::getSnapshotMergeStatus() {
+    return impl_.GetSnapshotMergeStatus();
+}
+
+// Methods from ::android::hardware::boot::V1_2::IBootControl.
+Return<uint32_t> BootControl::getActiveBootSlot() {
+    return impl_.GetActiveBootSlot();
+}
+
+IBootControl* HIDL_FETCH_IBootControl(const char* /* hal */) {
+    auto module = std::make_unique<BootControl>();
+    if (!module->Init()) {
+        ALOGE("Could not initialize BootControl module");
+        return nullptr;
+    }
+    return module.release();
+}
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/BootControl.h b/boot/1.2/default/BootControl.h
new file mode 100644
index 0000000..5791699
--- /dev/null
+++ b/boot/1.2/default/BootControl.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.
+ */
+
+#pragma once
+
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <hidl/MQDescriptor.h>
+#include <hidl/Status.h>
+#include <libboot_control/libboot_control.h>
+
+namespace android {
+namespace hardware {
+namespace boot {
+namespace V1_2 {
+namespace implementation {
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::BoolResult;
+using ::android::hardware::boot::V1_1::MergeStatus;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootControl : public IBootControl {
+  public:
+    bool Init();
+
+    // Methods from ::android::hardware::boot::V1_0::IBootControl.
+    Return<uint32_t> getNumberSlots() override;
+    Return<uint32_t> getCurrentSlot() override;
+    Return<void> markBootSuccessful(markBootSuccessful_cb _hidl_cb) override;
+    Return<void> setActiveBootSlot(uint32_t slot, setActiveBootSlot_cb _hidl_cb) override;
+    Return<void> setSlotAsUnbootable(uint32_t slot, setSlotAsUnbootable_cb _hidl_cb) override;
+    Return<BoolResult> isSlotBootable(uint32_t slot) override;
+    Return<BoolResult> isSlotMarkedSuccessful(uint32_t slot) override;
+    Return<void> getSuffix(uint32_t slot, getSuffix_cb _hidl_cb) override;
+
+    // Methods from ::android::hardware::boot::V1_1::IBootControl.
+    Return<bool> setSnapshotMergeStatus(MergeStatus status) override;
+    Return<MergeStatus> getSnapshotMergeStatus() override;
+
+    // Methods from ::android::hardware::boot::V1_2::IBootControl.
+    Return<uint32_t> getActiveBootSlot() override;
+
+  private:
+    android::bootable::BootControl impl_;
+};
+
+extern "C" IBootControl* HIDL_FETCH_IBootControl(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_2
+}  // namespace boot
+}  // namespace hardware
+}  // namespace android
diff --git a/boot/1.2/default/android.hardware.boot@1.2-service.rc b/boot/1.2/default/android.hardware.boot@1.2-service.rc
new file mode 100644
index 0000000..14926c0
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2-service.rc
@@ -0,0 +1,7 @@
+service vendor.boot-hal-1-2 /vendor/bin/hw/android.hardware.boot@1.2-service
+    interface android.hardware.boot@1.0::IBootControl default
+    interface android.hardware.boot@1.1::IBootControl default
+    interface android.hardware.boot@1.2::IBootControl default
+    class early_hal
+    user root
+    group root
diff --git a/boot/1.2/default/android.hardware.boot@1.2.xml b/boot/1.2/default/android.hardware.boot@1.2.xml
new file mode 100644
index 0000000..ba91e8f
--- /dev/null
+++ b/boot/1.2/default/android.hardware.boot@1.2.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="hidl">
+        <name>android.hardware.boot</name>
+        <transport>hwbinder</transport>
+        <fqname>@1.2::IBootControl/default</fqname>
+    </hal>
+</manifest>
diff --git a/boot/1.2/default/service.cpp b/boot/1.2/default/service.cpp
new file mode 100644
index 0000000..3053957
--- /dev/null
+++ b/boot/1.2/default/service.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "android.hardware.boot@1.2-service"
+
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <hidl/LegacySupport.h>
+
+using android::hardware::defaultPassthroughServiceImplementation;
+using IBootControl_V1_0 = android::hardware::boot::V1_0::IBootControl;
+using IBootControl_V1_2 = android::hardware::boot::V1_2::IBootControl;
+
+int main(int /* argc */, char* /* argv */[]) {
+    return defaultPassthroughServiceImplementation<IBootControl_V1_0, IBootControl_V1_2>();
+}
diff --git a/boot/1.2/vts/functional/Android.bp b/boot/1.2/vts/functional/Android.bp
new file mode 100644
index 0000000..7d546ed
--- /dev/null
+++ b/boot/1.2/vts/functional/Android.bp
@@ -0,0 +1,40 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalBootV1_2TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalBootV1_2TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.boot@1.0",
+        "android.hardware.boot@1.1",
+        "android.hardware.boot@1.2",
+        "libgmock",
+    ],
+    test_suites: [
+        "device-tests",
+        "vts",
+    ],
+}
diff --git a/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
new file mode 100644
index 0000000..9df23c3
--- /dev/null
+++ b/boot/1.2/vts/functional/VtsHalBootV1_2TargetTest.cpp
@@ -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.
+ */
+
+#define LOG_TAG "boot_hidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/hardware/boot/1.2/IBootControl.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+#include <unistd.h>
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::boot::V1_0::CommandResult;
+using ::android::hardware::boot::V1_0::Slot;
+using ::android::hardware::boot::V1_2::IBootControl;
+
+class BootHidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        boot = IBootControl::getService(GetParam());
+        ASSERT_NE(boot, nullptr);
+
+        LOG(INFO) << "Test is remote " << boot->isRemote();
+    }
+
+    sp<IBootControl> boot;
+};
+
+auto generate_callback(CommandResult* dest) {
+    return [=](CommandResult cr) { *dest = cr; };
+}
+
+TEST_P(BootHidlTest, GetActiveBootSlot) {
+    Slot curSlot = boot->getCurrentSlot();
+    Slot otherSlot = curSlot ? 0 : 1;
+
+    // Set the active slot, then check if the getter returns the correct slot.
+    CommandResult cr;
+    Return<void> result = boot->setActiveBootSlot(otherSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    Slot activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(otherSlot, activeSlot);
+
+    result = boot->setActiveBootSlot(curSlot, generate_callback(&cr));
+    EXPECT_TRUE(result.isOk());
+    activeSlot = boot->getActiveBootSlot();
+    EXPECT_EQ(curSlot, activeSlot);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BootHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, BootHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBootControl::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/broadcastradio/1.0/Android.bp b/broadcastradio/1.0/Android.bp
index 8239d74..fca4ac8 100644
--- a/broadcastradio/1.0/Android.bp
+++ b/broadcastradio/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.broadcastradio@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBroadcastRadio.hal",
diff --git a/broadcastradio/1.0/default/Android.bp b/broadcastradio/1.0/default/Android.bp
index 2c96e2a..efb7f6c 100644
--- a/broadcastradio/1.0/default/Android.bp
+++ b/broadcastradio/1.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.broadcastradio@1.0-impl",
     vendor: true,
diff --git a/broadcastradio/1.0/default/OWNERS b/broadcastradio/1.0/default/OWNERS
index b159083..57e6592 100644
--- a/broadcastradio/1.0/default/OWNERS
+++ b/broadcastradio/1.0/default/OWNERS
@@ -1,4 +1,3 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
 twasilczyk@google.com
diff --git a/broadcastradio/1.0/vts/functional/Android.bp b/broadcastradio/1.0/vts/functional/Android.bp
index 2a4f942..623ff78 100644
--- a/broadcastradio/1.0/vts/functional/Android.bp
+++ b/broadcastradio/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBroadcastradioV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
index 9897ab7..cac3dd0 100644
--- a/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
+++ b/broadcastradio/1.0/vts/functional/VtsHalBroadcastradioV1_0TargetTest.cpp
@@ -728,6 +728,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHidlTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, BroadcastRadioHidlTest,
         testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/broadcastradio/1.1/Android.bp b/broadcastradio/1.1/Android.bp
index 1cc9b62..2874a7d 100644
--- a/broadcastradio/1.1/Android.bp
+++ b/broadcastradio/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.broadcastradio@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBroadcastRadio.hal",
diff --git a/broadcastradio/1.1/default/Android.bp b/broadcastradio/1.1/default/Android.bp
index 3659cb9..5a17f5a 100644
--- a/broadcastradio/1.1/default/Android.bp
+++ b/broadcastradio/1.1/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.broadcastradio@1.1-service",
     init_rc: ["android.hardware.broadcastradio@1.1-service.rc"],
diff --git a/broadcastradio/1.1/vts/functional/Android.bp b/broadcastradio/1.1/vts/functional/Android.bp
index 661439a..f0dfe96 100644
--- a/broadcastradio/1.1/vts/functional/Android.bp
+++ b/broadcastradio/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBroadcastradioV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
index 4833beb..caf6cbd 100644
--- a/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
+++ b/broadcastradio/1.1/vts/functional/VtsHalBroadcastradioV1_1TargetTest.cpp
@@ -600,6 +600,7 @@
     } while (nextBand());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, BroadcastRadioHalTest,
         testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
diff --git a/broadcastradio/2.0/Android.bp b/broadcastradio/2.0/Android.bp
index 1040ba1..447b2c7 100644
--- a/broadcastradio/2.0/Android.bp
+++ b/broadcastradio/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.broadcastradio@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAnnouncementListener.hal",
diff --git a/broadcastradio/2.0/default/Android.bp b/broadcastradio/2.0/default/Android.bp
index 83eedb1..870c944 100644
--- a/broadcastradio/2.0/default/Android.bp
+++ b/broadcastradio/2.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.broadcastradio@2.0-service",
     init_rc: ["android.hardware.broadcastradio@2.0-service.rc"],
diff --git a/broadcastradio/2.0/vts/functional/Android.bp b/broadcastradio/2.0/vts/functional/Android.bp
index be17da3..e26486d 100644
--- a/broadcastradio/2.0/vts/functional/Android.bp
+++ b/broadcastradio/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalBroadcastradioV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
index 694d52a..615fde0 100644
--- a/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
+++ b/broadcastradio/2.0/vts/functional/VtsHalBroadcastradioV2_0TargetTest.cpp
@@ -415,7 +415,7 @@
 TEST_P(BroadcastRadioHalTest, FmTune) {
     ASSERT_TRUE(openSession());
 
-    uint64_t freq = 100100;  // 100.1 FM
+    uint64_t freq = 90900;  // 90.9 FM
     auto sel = make_selector_amfm(freq);
 
     /* TODO(b/69958777): there is a race condition between tune() and onCurrentProgramInfoChanged
@@ -486,6 +486,66 @@
 }
 
 /**
+ * Test tuning with DAB selector.
+ *
+ * Verifies that:
+ *  - if DAB selector is not supported, the method returns NOT_SUPPORTED;
+ *  - if it is supported, the method succeeds;
+ *  - after a successful tune call, onCurrentProgramInfoChanged callback is
+ *    invoked carrying a proper selector;
+ *  - program changes exactly to what was requested.
+ */
+TEST_P(BroadcastRadioHalTest, DabTune) {
+    Result halResult;
+    hidl_vec<DabTableEntry> config;
+    auto cb = [&](Result result, hidl_vec<DabTableEntry> configCb) {
+        halResult = result;
+        config = configCb;
+    };
+    auto hidlResult = mModule->getDabRegionConfig(cb);
+    ASSERT_TRUE(hidlResult.isOk());
+
+    if (halResult == Result::NOT_SUPPORTED) {
+        printSkipped("DAB not supported");
+        return;
+    }
+    ASSERT_EQ(Result::OK, halResult);
+    ASSERT_NE(config.size(), 0U);
+
+    ASSERT_TRUE(openSession());
+
+    ProgramSelector sel = {};
+    uint64_t freq = config[config.size() / 2].frequency;
+    sel.primaryId = make_identifier(IdentifierType::DAB_FREQUENCY,freq);
+
+    std::this_thread::sleep_for(gTuneWorkaround);
+
+    // try tuning
+    ProgramInfo infoCb = {};
+    EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_,
+                        InfoHasId(utils::make_identifier(IdentifierType::DAB_FREQUENCY, freq)))
+        .Times(AnyNumber())
+        .WillOnce(DoAll(SaveArg<0>(&infoCb), testing::Return(ByMove(Void()))));
+    auto result = mSession->tune(sel);
+
+    // expect a failure if it's not supported
+    if (!utils::isSupported(mProperties, sel)) {
+        EXPECT_EQ(Result::NOT_SUPPORTED, result);
+        return;
+    }
+
+    // expect a callback if it succeeds
+    EXPECT_EQ(Result::OK, result);
+    EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
+
+    LOG(DEBUG) << "current program info: " << toString(infoCb);
+
+    // it should tune exactly to what was requested
+    auto freqs = utils::getAllIds(infoCb.selector, IdentifierType::DAB_FREQUENCY);
+    EXPECT_NE(freqs.end(), find(freqs.begin(), freqs.end(), freq));
+}
+
+/**
  * Test tuning with empty program selector.
  *
  * Verifies that:
@@ -516,6 +576,12 @@
 
     EXPECT_TIMEOUT_CALL(*mCallback, onCurrentProgramInfoChanged_, _).Times(AnyNumber());
     auto result = mSession->scan(true /* up */, true /* skip subchannel */);
+
+    if (result == Result::NOT_SUPPORTED) {
+        printSkipped("seek not supported");
+        return;
+    }
+
     EXPECT_EQ(Result::OK, result);
     EXPECT_TIMEOUT_CALL_WAIT(*mCallback, onCurrentProgramInfoChanged_, timeout::tune);
 
@@ -565,6 +631,12 @@
 
     for (int i = 0; i < 10; i++) {
         auto result = mSession->scan(true /* up */, true /* skip subchannel */);
+
+        if (result == Result::NOT_SUPPORTED) {
+            printSkipped("cancel is skipped because of seek not supported");
+            return;
+        }
+
         ASSERT_EQ(Result::OK, result);
 
         auto cancelResult = mSession->cancel();
@@ -813,6 +885,7 @@
     closeHandle->close();
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BroadcastRadioHalTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, BroadcastRadioHalTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IBroadcastRadio::descriptor)),
diff --git a/broadcastradio/common/tests/Android.bp b/broadcastradio/common/tests/Android.bp
index 0ace941..0a6a3a2 100644
--- a/broadcastradio/common/tests/Android.bp
+++ b/broadcastradio/common/tests/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "android.hardware.broadcastradio@common-utils-xx-tests",
     vendor: true,
diff --git a/broadcastradio/common/utils/Android.bp b/broadcastradio/common/utils/Android.bp
index 32e06d7..3131ab3 100644
--- a/broadcastradio/common/utils/Android.bp
+++ b/broadcastradio/common/utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.broadcastradio@common-utils-lib",
     vendor_available: true,
diff --git a/broadcastradio/common/utils1x/Android.bp b/broadcastradio/common/utils1x/Android.bp
index 443dca1..681f7de 100644
--- a/broadcastradio/common/utils1x/Android.bp
+++ b/broadcastradio/common/utils1x/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.broadcastradio@common-utils-1x-lib",
     vendor_available: true,
diff --git a/broadcastradio/common/utils2x/Android.bp b/broadcastradio/common/utils2x/Android.bp
index df2cefe..124a7e5 100644
--- a/broadcastradio/common/utils2x/Android.bp
+++ b/broadcastradio/common/utils2x/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.broadcastradio@common-utils-2x-lib",
     vendor_available: true,
diff --git a/broadcastradio/common/vts/utils/Android.bp b/broadcastradio/common/vts/utils/Android.bp
index 24fea0b..e08813b 100644
--- a/broadcastradio/common/vts/utils/Android.bp
+++ b/broadcastradio/common/vts/utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.broadcastradio@vts-utils-lib",
     srcs: [
diff --git a/camera/common/1.0/Android.bp b/camera/common/1.0/Android.bp
index ed64060..3eb12b9 100644
--- a/camera/common/1.0/Android.bp
+++ b/camera/common/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.common@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/camera/common/1.0/default/Android.bp b/camera/common/1.0/default/Android.bp
index 3b8b239..4a5ca83 100644
--- a/camera/common/1.0/default/Android.bp
+++ b/camera/common/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.camera.common@1.0-helper",
     vendor_available: true,
diff --git a/camera/device/1.0/Android.bp b/camera/device/1.0/Android.bp
index 668884d..6947779 100644
--- a/camera/device/1.0/Android.bp
+++ b/camera/device/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDevice.hal",
diff --git a/camera/device/1.0/default/Android.bp b/camera/device/1.0/default/Android.bp
index da70577..9ff6480 100644
--- a/camera/device/1.0/default/Android.bp
+++ b/camera/device/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "camera.device@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/camera/device/1.0/default/CameraDevice.cpp b/camera/device/1.0/default/CameraDevice.cpp
index 2dd6094..80733d1 100644
--- a/camera/device/1.0/default/CameraDevice.cpp
+++ b/camera/device/1.0/default/CameraDevice.cpp
@@ -15,6 +15,9 @@
  */
 
 #define LOG_TAG "CamDev@1.0-impl"
+
+#include <fcntl.h>
+
 #include <hardware/camera.h>
 #include <hardware/gralloc1.h>
 #include <hidlmemory/mapping.h>
diff --git a/camera/device/3.2/Android.bp b/camera/device/3.2/Android.bp
index 2e5349f..c80538c 100644
--- a/camera/device/3.2/Android.bp
+++ b/camera/device/3.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@3.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDevice.hal",
diff --git a/camera/device/3.2/default/Android.bp b/camera/device/3.2/default/Android.bp
index be2de07..a196291 100644
--- a/camera/device/3.2/default/Android.bp
+++ b/camera/device/3.2/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "camera.device@3.2-impl",
     defaults: ["hidl_defaults"],
diff --git a/camera/device/3.2/default/CameraDeviceSession.cpp b/camera/device/3.2/default/CameraDeviceSession.cpp
index 99cdccb..769991c 100644
--- a/camera/device/3.2/default/CameraDeviceSession.cpp
+++ b/camera/device/3.2/default/CameraDeviceSession.cpp
@@ -790,8 +790,9 @@
             auto it = batch->mBatchBufs.find(buffer.streamId);
             if (it != batch->mBatchBufs.end()) {
                 InflightBatch::BufferBatch& bb = it->second;
+                auto id = buffer.streamId;
                 pushStreamBuffer(std::move(buffer), bb.mBuffers);
-                filledStreams.push_back(buffer.streamId);
+                filledStreams.push_back(id);
             } else {
                 pushStreamBuffer(std::move(buffer), nonBatchedBuffers);
             }
diff --git a/camera/device/3.3/Android.bp b/camera/device/3.3/Android.bp
index 679fad6..f5e51d6 100644
--- a/camera/device/3.3/Android.bp
+++ b/camera/device/3.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@3.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDeviceSession.hal",
diff --git a/camera/device/3.3/default/Android.bp b/camera/device/3.3/default/Android.bp
index 0aa0dd7..cc0dd39 100644
--- a/camera/device/3.3/default/Android.bp
+++ b/camera/device/3.3/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "camera.device@3.3-impl",
     defaults: ["hidl_defaults"],
diff --git a/camera/device/3.4/Android.bp b/camera/device/3.4/Android.bp
index e6f42d6..2a6faab 100644
--- a/camera/device/3.4/Android.bp
+++ b/camera/device/3.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@3.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDeviceCallback.hal",
diff --git a/camera/device/3.4/default/Android.bp b/camera/device/3.4/default/Android.bp
index 982dce1..9f0c777 100644
--- a/camera/device/3.4/default/Android.bp
+++ b/camera/device/3.4/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "camera.device@3.4-impl_headers",
     vendor: true,
diff --git a/camera/device/3.4/default/CameraDeviceSession.cpp b/camera/device/3.4/default/CameraDeviceSession.cpp
index b4ebe22..3f088a3 100644
--- a/camera/device/3.4/default/CameraDeviceSession.cpp
+++ b/camera/device/3.4/default/CameraDeviceSession.cpp
@@ -656,8 +656,9 @@
             auto it = batch->mBatchBufs.find(buffer.streamId);
             if (it != batch->mBatchBufs.end()) {
                 InflightBatch::BufferBatch& bb = it->second;
+                auto id = buffer.streamId;
                 pushStreamBuffer(std::move(buffer), bb.mBuffers);
-                filledStreams.push_back(buffer.streamId);
+                filledStreams.push_back(id);
             } else {
                 pushStreamBuffer(std::move(buffer), nonBatchedBuffers);
             }
diff --git a/camera/device/3.5/Android.bp b/camera/device/3.5/Android.bp
index 362a5e6..f29f936 100644
--- a/camera/device/3.5/Android.bp
+++ b/camera/device/3.5/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@3.5",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDevice.hal",
diff --git a/camera/device/3.5/default/Android.bp b/camera/device/3.5/default/Android.bp
index d106b4b..9d27b32 100644
--- a/camera/device/3.5/default/Android.bp
+++ b/camera/device/3.5/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "camera.device@3.5-impl_headers",
     vendor: true,
diff --git a/camera/device/3.6/Android.bp b/camera/device/3.6/Android.bp
index 19adb34..ff37ca3 100644
--- a/camera/device/3.6/Android.bp
+++ b/camera/device/3.6/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.device@3.6",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraDevice.hal",
diff --git a/camera/device/3.6/default/Android.bp b/camera/device/3.6/default/Android.bp
index 2871e2a..89ee145 100644
--- a/camera/device/3.6/default/Android.bp
+++ b/camera/device/3.6/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "camera.device@3.6-external-impl_headers",
     vendor: true,
diff --git a/camera/metadata/3.2/Android.bp b/camera/metadata/3.2/Android.bp
index f58fb28..ec8107e 100644
--- a/camera/metadata/3.2/Android.bp
+++ b/camera/metadata/3.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.metadata@3.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/camera/metadata/3.3/Android.bp b/camera/metadata/3.3/Android.bp
index 885f4f9..4bed25b 100644
--- a/camera/metadata/3.3/Android.bp
+++ b/camera/metadata/3.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.metadata@3.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/camera/metadata/3.4/Android.bp b/camera/metadata/3.4/Android.bp
index 6a92458..fdddfdf 100644
--- a/camera/metadata/3.4/Android.bp
+++ b/camera/metadata/3.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.metadata@3.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/camera/metadata/3.5/Android.bp b/camera/metadata/3.5/Android.bp
index 224c369..9349d54 100644
--- a/camera/metadata/3.5/Android.bp
+++ b/camera/metadata/3.5/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.metadata@3.5",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/camera/provider/2.4/Android.bp b/camera/provider/2.4/Android.bp
index 876814d..a4c0dd2 100644
--- a/camera/provider/2.4/Android.bp
+++ b/camera/provider/2.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.provider@2.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ICameraProvider.hal",
         "ICameraProviderCallback.hal",
diff --git a/camera/provider/2.4/default/Android.bp b/camera/provider/2.4/default/Android.bp
index 627ddf4..bccd6cb 100644
--- a/camera/provider/2.4/default/Android.bp
+++ b/camera/provider/2.4/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.camera.provider@2.4-legacy",
     defaults: ["hidl_defaults"],
@@ -76,7 +85,7 @@
     header_libs: [
         "camera.device@3.4-external-impl_headers",
         "camera.device@3.5-external-impl_headers",
-        "camera.device@3.6-external-impl_headers"
+        "camera.device@3.6-external-impl_headers",
     ],
     export_include_dirs: ["."],
 }
@@ -150,6 +159,7 @@
         "android.hidl.memory@1.0",
         "libbinder",
         "libcamera_metadata",
+        "libcutils",
         "libhardware",
         "libhidlbase",
         "liblog",
diff --git a/camera/provider/2.4/default/service.cpp b/camera/provider/2.4/default/service.cpp
index 4475f7d..0a4f787 100644
--- a/camera/provider/2.4/default/service.cpp
+++ b/camera/provider/2.4/default/service.cpp
@@ -22,7 +22,9 @@
 
 #include <android/hardware/camera/provider/2.4/ICameraProvider.h>
 #include <binder/ProcessState.h>
+#include <cutils/properties.h>
 #include <hidl/LegacySupport.h>
+#include <malloc.h>
 
 using android::status_t;
 using android::hardware::defaultLazyPassthroughServiceImplementation;
@@ -41,6 +43,18 @@
     // The camera HAL may communicate to other vendor components via
     // /dev/vndbinder
     android::ProcessState::initWithDriver("/dev/vndbinder");
+
+    // b/166675194
+    if (property_get_bool("ro.vendor.camera.provider24.disable_mem_init", false)) {
+        if (mallopt(M_BIONIC_ZERO_INIT, 0) == 0) {
+            // Note - heap initialization is only present on devices with Scudo.
+            // Devices with jemalloc don't have heap-init, and thus the mallopt
+            // will fail. On these devices, you probably just want to remove the
+            // property.
+            ALOGE("Disabling heap initialization failed.");
+        }
+    }
+
     status_t status;
     if (kLazyService) {
         status = defaultLazyPassthroughServiceImplementation<ICameraProvider>("legacy/0",
diff --git a/camera/provider/2.4/vts/functional/Android.bp b/camera/provider/2.4/vts/functional/Android.bp
index cd66f74..691d772 100644
--- a/camera/provider/2.4/vts/functional/Android.bp
+++ b/camera/provider/2.4/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalCameraProviderV2_4TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
index 560b24e..7e6b5ad 100644
--- a/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
+++ b/camera/provider/2.4/vts/functional/VtsHalCameraProviderV2_4TargetTest.cpp
@@ -1211,7 +1211,12 @@
             return notify;
         }
 
-        if (physicalCameraMetadata.size() != request->expectedPhysicalResults.size()) {
+        // Physical device results are only expected in the last/final
+        // partial result notification.
+        bool expectPhysicalResults = !(request->usePartialResult &&
+                (results.partialResult < request->numPartialResults));
+        if (expectPhysicalResults &&
+                (physicalCameraMetadata.size() != request->expectedPhysicalResults.size())) {
             ALOGE("%s: Frame %d: Returned physical metadata count %zu "
                     "must be equal to expected count %zu", __func__, frameNumber,
                     physicalCameraMetadata.size(), request->expectedPhysicalResults.size());
@@ -1672,7 +1677,7 @@
     Return<void> ret;
     ::android::sp<ICameraDevice> device3_x;
     bool retVal = false;
-    if (getCameraDeviceVersion(mProviderType, name) == CAMERA_DEVICE_API_VERSION_1_0) {
+    if (getCameraDeviceVersion(name, mProviderType) == CAMERA_DEVICE_API_VERSION_1_0) {
         return false;
     }
     ret = provider->getCameraDeviceInterface_V3_x(name, [&](auto status, const auto& device) {
@@ -3337,8 +3342,8 @@
             size_t j = 0;
             for (const auto& it : outputStreams) {
                 V3_2::Stream stream3_2;
-                V3_2::DataspaceFlags dataspaceFlag =
-                        getDataspace(static_cast<PixelFormat>(it.format));
+                V3_2::DataspaceFlags dataspaceFlag = getDataspace(
+                        static_cast<PixelFormat>(it.format));
                 stream3_2 = {streamId++,
                              StreamType::OUTPUT,
                              static_cast<uint32_t>(it.width),
@@ -5889,7 +5894,7 @@
         case PixelFormat::RAW_OPAQUE:
         case PixelFormat::RAW10:
         case PixelFormat::RAW12:
-            return static_cast<V3_2::DataspaceFlags>(Dataspace::ARBITRARY);
+            return  static_cast<V3_2::DataspaceFlags>(Dataspace::ARBITRARY);
         default:
             return static_cast<V3_2::DataspaceFlags>(Dataspace::UNKNOWN);
     }
@@ -6269,8 +6274,8 @@
     ASSERT_EQ(Status::OK, rc);
     ASSERT_FALSE(outputStreams.empty());
 
-    V3_2::DataspaceFlags dataspaceFlag =
-            getDataspace(static_cast<PixelFormat>(outputStreams[idx].format));
+    V3_2::DataspaceFlags dataspaceFlag = getDataspace(
+            static_cast<PixelFormat>(outputStreams[idx].format));
 
     ::android::hardware::hidl_vec<V3_4::Stream> streams3_4(/*size*/1);
     V3_4::Stream stream3_4 = {{ 0 /*streamId*/, StreamType::OUTPUT,
diff --git a/camera/provider/2.5/Android.bp b/camera/provider/2.5/Android.bp
index 4ca1efb..e14c0a8 100644
--- a/camera/provider/2.5/Android.bp
+++ b/camera/provider/2.5/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.provider@2.5",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraProvider.hal",
diff --git a/camera/provider/2.5/default/Android.bp b/camera/provider/2.5/default/Android.bp
index 9ddf651..2fcb35a 100644
--- a/camera/provider/2.5/default/Android.bp
+++ b/camera/provider/2.5/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.camera.provider@2.5-legacy",
     proprietary: true,
diff --git a/camera/provider/2.6/Android.bp b/camera/provider/2.6/Android.bp
index e69819c..f402a56 100644
--- a/camera/provider/2.6/Android.bp
+++ b/camera/provider/2.6/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.camera.provider@2.6",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICameraProvider.hal",
diff --git a/cas/1.0/Android.bp b/cas/1.0/Android.bp
index 4982e20..368156a 100644
--- a/cas/1.0/Android.bp
+++ b/cas/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.cas@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICas.hal",
diff --git a/cas/1.0/default/Android.bp b/cas/1.0/default/Android.bp
index f9977ff..e8b1a06 100644
--- a/cas/1.0/default/Android.bp
+++ b/cas/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "cas_service_defaults",
     defaults: ["hidl_defaults"],
@@ -12,8 +21,6 @@
       "TypeConvert.cpp",
     ],
 
-    compile_multilib: "32",
-
     shared_libs: [
       "android.hardware.cas@1.0",
       "android.hardware.cas.native@1.0",
diff --git a/cas/1.0/default/DescramblerImpl.cpp b/cas/1.0/default/DescramblerImpl.cpp
index 9b09751..f79b32d 100644
--- a/cas/1.0/default/DescramblerImpl.cpp
+++ b/cas/1.0/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "android.hardware.cas@1.0-DescramblerImpl"
 
 #include <hidlmemory/mapping.h>
+#include <inttypes.h>
 #include <media/cas/DescramblerAPI.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/AString.h>
@@ -101,7 +102,7 @@
     // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
     // but the mapped memory's actual size will be smaller than the reported size.
     if (srcBuffer.heapBase.size() > SIZE_MAX) {
-        ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+        ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
         android_errorWriteLog(0x534e4554, "79376389");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -118,8 +119,8 @@
     }
     if (!validateRangeForSize(
             srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
-        ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
-                srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+        ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+              "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -135,8 +136,8 @@
     // is consistent with the source shared buffer size.
     if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
         ALOGE("Invalid srcOffset and subsample size: "
-                "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-                srcOffset, totalBytesInSubSamples, srcBuffer.size);
+                "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+                "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -153,8 +154,8 @@
         // dstOffset against the buffer size too.
         if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
             ALOGE("Invalid dstOffset and subsample size: "
-                    "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-                    dstOffset, totalBytesInSubSamples, srcBuffer.size);
+                    "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+                    "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
             android_errorWriteLog(0x534e4554, "67962232");
             _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
             return Void();
diff --git a/cas/1.0/default/android.hardware.cas@1.0-service-lazy.rc b/cas/1.0/default/android.hardware.cas@1.0-service-lazy.rc
index 443549a..622ee8f 100644
--- a/cas/1.0/default/android.hardware.cas@1.0-service-lazy.rc
+++ b/cas/1.0/default/android.hardware.cas@1.0-service-lazy.rc
@@ -6,4 +6,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.0/default/android.hardware.cas@1.0-service.rc b/cas/1.0/default/android.hardware.cas@1.0-service.rc
index 74f2f96..5df4825 100644
--- a/cas/1.0/default/android.hardware.cas@1.0-service.rc
+++ b/cas/1.0/default/android.hardware.cas@1.0-service.rc
@@ -3,4 +3,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.0/vts/functional/Android.bp b/cas/1.0/vts/functional/Android.bp
index 82dc568..fbfb7a7 100644
--- a/cas/1.0/vts/functional/Android.bp
+++ b/cas/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalCasV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -31,4 +40,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/cas/1.1/Android.bp b/cas/1.1/Android.bp
index 13217b6..f5b1cc9 100644
--- a/cas/1.1/Android.bp
+++ b/cas/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.cas@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ICas.hal",
         "ICasListener.hal",
diff --git a/cas/1.1/default/Android.bp b/cas/1.1/default/Android.bp
index 66a1eb8..f603573 100644
--- a/cas/1.1/default/Android.bp
+++ b/cas/1.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "cas_service_defaults@1.1",
     defaults: ["hidl_defaults"],
@@ -12,8 +21,6 @@
       "TypeConvert.cpp",
     ],
 
-    compile_multilib: "32",
-
     shared_libs: [
       "android.hardware.cas@1.0",
       "android.hardware.cas@1.1",
diff --git a/cas/1.1/default/DescramblerImpl.cpp b/cas/1.1/default/DescramblerImpl.cpp
index 36dc1a5..309cd3c 100644
--- a/cas/1.1/default/DescramblerImpl.cpp
+++ b/cas/1.1/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
 
 #include <hidlmemory/mapping.h>
+#include <inttypes.h>
 #include <media/cas/DescramblerAPI.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -92,7 +93,7 @@
     // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
     // but the mapped memory's actual size will be smaller than the reported size.
     if (srcBuffer.heapBase.size() > SIZE_MAX) {
-        ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+        ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
         android_errorWriteLog(0x534e4554, "79376389");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -108,8 +109,8 @@
         return Void();
     }
     if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
-        ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
-              srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+        ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+              "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -125,8 +126,8 @@
     // is consistent with the source shared buffer size.
     if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
         ALOGE("Invalid srcOffset and subsample size: "
-              "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-              srcOffset, totalBytesInSubSamples, srcBuffer.size);
+              "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+              "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -143,8 +144,8 @@
         // dstOffset against the buffer size too.
         if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
             ALOGE("Invalid dstOffset and subsample size: "
-                  "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-                  dstOffset, totalBytesInSubSamples, srcBuffer.size);
+                  "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+                  "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
             android_errorWriteLog(0x534e4554, "67962232");
             _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
             return Void();
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
index 73c505d..0721dc3 100644
--- a/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
+++ b/cas/1.1/default/android.hardware.cas@1.1-service-lazy.rc
@@ -7,4 +7,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.1/default/android.hardware.cas@1.1-service.rc b/cas/1.1/default/android.hardware.cas@1.1-service.rc
index 4081fe1..132d943 100644
--- a/cas/1.1/default/android.hardware.cas@1.1-service.rc
+++ b/cas/1.1/default/android.hardware.cas@1.1-service.rc
@@ -3,4 +3,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.1/vts/functional/Android.bp b/cas/1.1/vts/functional/Android.bp
index de223c8..4873157 100644
--- a/cas/1.1/vts/functional/Android.bp
+++ b/cas/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalCasV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -32,4 +41,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/cas/1.2/Android.bp b/cas/1.2/Android.bp
index fbb38b0..6c94020 100644
--- a/cas/1.2/Android.bp
+++ b/cas/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.cas@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICas.hal",
diff --git a/cas/1.2/default/Android.bp b/cas/1.2/default/Android.bp
index 9e53148..f4d68ed 100644
--- a/cas/1.2/default/Android.bp
+++ b/cas/1.2/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "cas_service_defaults@1.2",
     defaults: ["hidl_defaults"],
@@ -12,8 +21,6 @@
       "TypeConvert.cpp",
     ],
 
-    compile_multilib: "32",
-
     shared_libs: [
       "android.hardware.cas@1.0",
       "android.hardware.cas@1.1",
diff --git a/cas/1.2/default/DescramblerImpl.cpp b/cas/1.2/default/DescramblerImpl.cpp
index 36dc1a5..309cd3c 100644
--- a/cas/1.2/default/DescramblerImpl.cpp
+++ b/cas/1.2/default/DescramblerImpl.cpp
@@ -18,6 +18,7 @@
 #define LOG_TAG "android.hardware.cas@1.1-DescramblerImpl"
 
 #include <hidlmemory/mapping.h>
+#include <inttypes.h>
 #include <media/cas/DescramblerAPI.h>
 #include <media/hardware/CryptoAPI.h>
 #include <media/stagefright/foundation/AUtils.h>
@@ -92,7 +93,7 @@
     // size in size_t. If size is over SIZE_MAX, mapMemory mapMemory could succeed
     // but the mapped memory's actual size will be smaller than the reported size.
     if (srcBuffer.heapBase.size() > SIZE_MAX) {
-        ALOGE("Invalid hidl_memory size: %llu", srcBuffer.heapBase.size());
+        ALOGE("Invalid hidl_memory size: %" PRIu64 "", srcBuffer.heapBase.size());
         android_errorWriteLog(0x534e4554, "79376389");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -108,8 +109,8 @@
         return Void();
     }
     if (!validateRangeForSize(srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize())) {
-        ALOGE("Invalid src buffer range: offset %llu, size %llu, srcMem size %llu",
-              srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
+        ALOGE("Invalid src buffer range: offset %" PRIu64 ", size %" PRIu64 ", srcMem"
+              "size %" PRIu64 "", srcBuffer.offset, srcBuffer.size, (uint64_t)srcMem->getSize());
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -125,8 +126,8 @@
     // is consistent with the source shared buffer size.
     if (!validateRangeForSize(srcOffset, totalBytesInSubSamples, srcBuffer.size)) {
         ALOGE("Invalid srcOffset and subsample size: "
-              "srcOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-              srcOffset, totalBytesInSubSamples, srcBuffer.size);
+              "srcOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+              "size %" PRIu64 "", srcOffset, totalBytesInSubSamples, srcBuffer.size);
         android_errorWriteLog(0x534e4554, "67962232");
         _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
         return Void();
@@ -143,8 +144,8 @@
         // dstOffset against the buffer size too.
         if (!validateRangeForSize(dstOffset, totalBytesInSubSamples, srcBuffer.size)) {
             ALOGE("Invalid dstOffset and subsample size: "
-                  "dstOffset %llu, totalBytesInSubSamples %llu, srcBuffer size %llu",
-                  dstOffset, totalBytesInSubSamples, srcBuffer.size);
+                  "dstOffset %" PRIu64 ", totalBytesInSubSamples %" PRIu64 ", srcBuffer"
+                  "size %" PRIu64 "", dstOffset, totalBytesInSubSamples, srcBuffer.size);
             android_errorWriteLog(0x534e4554, "67962232");
             _hidl_cb(toStatus(BAD_VALUE), 0, NULL);
             return Void();
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc
index 1c75100..d91fdce 100644
--- a/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc
+++ b/cas/1.2/default/android.hardware.cas@1.2-service-lazy.rc
@@ -8,4 +8,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.2/default/android.hardware.cas@1.2-service.rc b/cas/1.2/default/android.hardware.cas@1.2-service.rc
index d1c853e..b22971a 100644
--- a/cas/1.2/default/android.hardware.cas@1.2-service.rc
+++ b/cas/1.2/default/android.hardware.cas@1.2-service.rc
@@ -3,4 +3,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/cas/1.2/vts/functional/Android.bp b/cas/1.2/vts/functional/Android.bp
index 74ea85f..21f791b 100644
--- a/cas/1.2/vts/functional/Android.bp
+++ b/cas/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalCasV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/cas/native/1.0/Android.bp b/cas/native/1.0/Android.bp
index 633ceb9..3bb74c1 100644
--- a/cas/native/1.0/Android.bp
+++ b/cas/native/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.cas.native@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDescrambler.hal",
diff --git a/common/TEST_MAPPING b/common/TEST_MAPPING
new file mode 100644
index 0000000..7dd29e5
--- /dev/null
+++ b/common/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "libaidlcommonsupport_test"
+    }
+  ]
+}
diff --git a/common/aidl/Android.bp b/common/aidl/Android.bp
index 9ea4cdf..912bfdc 100644
--- a/common/aidl/Android.bp
+++ b/common/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.common",
     host_supported: true,
@@ -6,13 +15,14 @@
         enabled: true,
         support_system_process: true,
     },
+    vndk_use_version: "2",
     srcs: [
         "android/hardware/common/*.aidl",
     ],
     stability: "vintf",
     backend: {
         java: {
-            enabled: false,
+            sdk_version: "module_current",
         },
         cpp: {
             enabled: false,
@@ -21,6 +31,7 @@
             apex_available: [
                 "//apex_available:platform",
                 "com.android.media.swcodec",
+                "com.android.neuralnetworks",
             ],
             min_sdk_version: "29",
         },
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
new file mode 100644
index 0000000..a438031
--- /dev/null
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/Ashmem.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common;
+@VintfStability
+parcelable Ashmem {
+  ParcelFileDescriptor fd;
+  long size;
+}
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl
new file mode 100644
index 0000000..394ea8f
--- /dev/null
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/MappableFile.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common;
+@VintfStability
+parcelable MappableFile {
+  long length;
+  int prot;
+  ParcelFileDescriptor fd;
+  long offset;
+}
diff --git a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
index f37b7d5..2ed5c0b 100644
--- a/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
+++ b/common/aidl/aidl_api/android.hardware.common/current/android/hardware/common/NativeHandle.aidl
@@ -1,14 +1,30 @@
+/*
+ * Copyright 2019 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.
+ */
 ///////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/common/aidl/android/hardware/common/Ashmem.aidl b/common/aidl/android/hardware/common/Ashmem.aidl
new file mode 100644
index 0000000..8e40266
--- /dev/null
+++ b/common/aidl/android/hardware/common/Ashmem.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * Type that holds same memory as the "ashmem" hidl_memory type from HIDL.
+ */
+@VintfStability
+parcelable Ashmem {
+    /**
+     * A handle to a memory region.
+     */
+    ParcelFileDescriptor fd;
+    /**
+     * Size of the memory region in bytes.
+     */
+    long size;
+}
diff --git a/common/aidl/android/hardware/common/MappableFile.aidl b/common/aidl/android/hardware/common/MappableFile.aidl
new file mode 100644
index 0000000..a7763ea
--- /dev/null
+++ b/common/aidl/android/hardware/common/MappableFile.aidl
@@ -0,0 +1,53 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * A region of a file that can be mapped into memory.
+ *
+ * In Linux, MappableFile may be used with mmap as `MAP_SHARED`.
+ *
+ * MappableFile is compatible with ::android::base::MappedFile.
+ */
+@VintfStability
+parcelable MappableFile {
+    /**
+     * Length of the mapping region in bytes.
+     */
+    long length;
+    /**
+     * The desired memory protection for the mapping.
+     *
+     * In Linux, prot is either `PROT_NONE` (indicating that mapped pages may not be accessed) or
+     * the bitwise OR of one or more of the following flags:
+     * - `PROT_READ` (indicating that the mapped pages may be read)
+     * - `PROT_WRITE` (indicating that the mapped pages may be written)
+     */
+    int prot;
+    /**
+     * A handle to a mappable file.
+     */
+    ParcelFileDescriptor fd;
+    /**
+     * The offset in the file to the beginning of the mapping region in number of bytes.
+     *
+     * Note: Some mapping functions require that the offset is aligned to the page size.
+     */
+    long offset;
+}
diff --git a/common/fmq/aidl/Android.bp b/common/fmq/aidl/Android.bp
new file mode 100644
index 0000000..1ab724f
--- /dev/null
+++ b/common/fmq/aidl/Android.bp
@@ -0,0 +1,33 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.common.fmq",
+    host_supported: true,
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+        support_system_process: true,
+    },
+    srcs: [
+        "android/hardware/common/fmq/*.aidl",
+    ],
+    imports: [
+        "android.hardware.common",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: false,
+        },
+    },
+}
diff --git a/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/GrantorDescriptor.aidl b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/GrantorDescriptor.aidl
new file mode 100644
index 0000000..cf7048b
--- /dev/null
+++ b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/GrantorDescriptor.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common.fmq;
+@VintfStability
+parcelable GrantorDescriptor {
+  int fdIndex;
+  int offset;
+  long extent;
+}
diff --git a/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/MQDescriptor.aidl b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/MQDescriptor.aidl
new file mode 100644
index 0000000..add4b64
--- /dev/null
+++ b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/MQDescriptor.aidl
@@ -0,0 +1,41 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common.fmq;
+@VintfStability
+parcelable MQDescriptor<T, Flavor> {
+  android.hardware.common.fmq.GrantorDescriptor[] grantors;
+  android.hardware.common.NativeHandle handle;
+  int quantum;
+  int flags;
+}
diff --git a/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/SynchronizedReadWrite.aidl b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/SynchronizedReadWrite.aidl
new file mode 100644
index 0000000..12c61ba
--- /dev/null
+++ b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/SynchronizedReadWrite.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common.fmq;
+@VintfStability
+enum SynchronizedReadWrite {
+  EMPTY = 0,
+}
diff --git a/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/UnsynchronizedWrite.aidl b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/UnsynchronizedWrite.aidl
new file mode 100644
index 0000000..f99528d
--- /dev/null
+++ b/common/fmq/aidl/aidl_api/android.hardware.common.fmq/current/android/hardware/common/fmq/UnsynchronizedWrite.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.common.fmq;
+@VintfStability
+enum UnsynchronizedWrite {
+  EMPTY = 0,
+}
diff --git a/common/fmq/aidl/android/hardware/common/fmq/GrantorDescriptor.aidl b/common/fmq/aidl/android/hardware/common/fmq/GrantorDescriptor.aidl
new file mode 100644
index 0000000..672415e
--- /dev/null
+++ b/common/fmq/aidl/android/hardware/common/fmq/GrantorDescriptor.aidl
@@ -0,0 +1,36 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common.fmq;
+
+/*
+ * Included in MQDescriptor, for use with libfmq.
+ */
+@VintfStability
+parcelable GrantorDescriptor {
+    /*
+     * Index of file descriptor for this grantor
+     */
+    int fdIndex;
+    /*
+     * The offset of this descriptor in the shared memory in bytes.
+     */
+    int offset;
+    /*
+     * The size of this descriptor in bytes.
+     */
+    long extent;
+}
diff --git a/common/fmq/aidl/android/hardware/common/fmq/MQDescriptor.aidl b/common/fmq/aidl/android/hardware/common/fmq/MQDescriptor.aidl
new file mode 100644
index 0000000..46622f0
--- /dev/null
+++ b/common/fmq/aidl/android/hardware/common/fmq/MQDescriptor.aidl
@@ -0,0 +1,47 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common.fmq;
+
+import android.hardware.common.NativeHandle;
+import android.hardware.common.fmq.GrantorDescriptor;
+
+/*
+ * For use with libfmq. This is created from an instance of AidlMessageQueue,
+ * and is used to pass information required to create another instance of that
+ * queue for fast communication.
+ * T - is used to specify the type of the payload
+ * Flavor - is used to specify the type of the queue using
+ * android.hardware.common.SynchronizedReadWrite or UnsynchronizedWrite
+ */
+@VintfStability
+parcelable MQDescriptor<T, Flavor> {
+    /*
+     * Describes each of the grantors for the message queue. They are used to
+     * get the readptr, writeptr, dataptr, and the optional EventFlag word
+     * for blocking operations in the shared memory.
+     */
+    GrantorDescriptor[] grantors;
+    /*
+     * NativeHandle that contains the file descriptors for shared memory used
+     * in the message queue
+     */
+    NativeHandle handle;
+    /* Size of each item, T, in bytes */
+    int quantum;
+    /* EventFlag word for blocking operations */
+    int flags;
+}
diff --git a/common/fmq/aidl/android/hardware/common/fmq/SynchronizedReadWrite.aidl b/common/fmq/aidl/android/hardware/common/fmq/SynchronizedReadWrite.aidl
new file mode 100644
index 0000000..8c33442
--- /dev/null
+++ b/common/fmq/aidl/android/hardware/common/fmq/SynchronizedReadWrite.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common.fmq;
+
+/*
+ * For use with android.hardware.common.MQDescriptor to specify which type of
+ * queue to use. SynchronizedReadWrite is single reader, single writer, with no
+ * overflow. All messages written need to be read.
+ */
+@VintfStability
+enum SynchronizedReadWrite {
+    EMPTY,
+}
diff --git a/common/fmq/aidl/android/hardware/common/fmq/UnsynchronizedWrite.aidl b/common/fmq/aidl/android/hardware/common/fmq/UnsynchronizedWrite.aidl
new file mode 100644
index 0000000..24c4cce
--- /dev/null
+++ b/common/fmq/aidl/android/hardware/common/fmq/UnsynchronizedWrite.aidl
@@ -0,0 +1,27 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.common.fmq;
+
+/*
+ * For use with android.hardware.common.MQDescriptor to specify which type of
+ * queue to use. UnsynchronizedWrite is single writer, multiple reader, with
+ * overflow. If messages are not read fast enough, they can be overwritten.
+ */
+@VintfStability
+enum UnsynchronizedWrite {
+    EMPTY,
+}
diff --git a/common/support/Android.bp b/common/support/Android.bp
new file mode 100644
index 0000000..730798d
--- /dev/null
+++ b/common/support/Android.bp
@@ -0,0 +1,41 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "libaidlcommonsupport",
+    vendor_available: true,
+    host_supported: true,
+    defaults: ["libbinder_ndk_host_user"],
+    srcs: ["NativeHandle.cpp"],
+    export_include_dirs: ["include"],
+    shared_libs: [
+        "android.hardware.common-V2-ndk_platform",
+        "libcutils",
+    ],
+    apex_available: [
+        "//apex_available:platform",
+        "com.android.neuralnetworks",
+    ],
+    min_sdk_version: "29",
+}
+
+cc_test {
+    name: "libaidlcommonsupport_test",
+    host_supported: true,
+    defaults: ["libbinder_ndk_host_user"],
+    srcs: ["test.cpp"],
+    static_libs: [
+        "android.hardware.common-V2-ndk_platform",
+        "libaidlcommonsupport",
+    ],
+    shared_libs: [
+        "libcutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/common/support/NativeHandle.cpp b/common/support/NativeHandle.cpp
new file mode 100644
index 0000000..321d7a8
--- /dev/null
+++ b/common/support/NativeHandle.cpp
@@ -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.
+ */
+
+#include <aidlcommonsupport/NativeHandle.h>
+
+#include <fcntl.h>
+
+namespace android {
+
+using aidl::android::hardware::common::NativeHandle;
+
+static native_handle_t* fromAidl(const NativeHandle& handle, bool doDup) {
+    native_handle_t* to = native_handle_create(handle.fds.size(), handle.ints.size());
+    if (!to) return nullptr;
+
+    for (size_t i = 0; i < handle.fds.size(); i++) {
+        int fd = handle.fds[i].get();
+        to->data[i] = doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd;
+    }
+    memcpy(to->data + handle.fds.size(), handle.ints.data(), handle.ints.size() * sizeof(int));
+    return to;
+}
+
+native_handle_t* makeFromAidl(const NativeHandle& handle) {
+    return fromAidl(handle, false /* doDup */);
+}
+native_handle_t* dupFromAidl(const NativeHandle& handle) {
+    return fromAidl(handle, true /* doDup */);
+}
+
+static NativeHandle toAidl(const native_handle_t* handle, bool doDup) {
+    NativeHandle to;
+
+    to.fds = std::vector<ndk::ScopedFileDescriptor>(handle->numFds);
+    for (size_t i = 0; i < handle->numFds; i++) {
+        int fd = handle->data[i];
+        to.fds.at(i).set(doDup ? fcntl(fd, F_DUPFD_CLOEXEC, 0) : fd);
+    }
+
+    to.ints = std::vector<int32_t>(handle->data + handle->numFds,
+                                   handle->data + handle->numFds + handle->numInts);
+    return to;
+}
+
+NativeHandle makeToAidl(const native_handle_t* handle) {
+    return toAidl(handle, false /* doDup */);
+}
+
+NativeHandle dupToAidl(const native_handle_t* handle) {
+    return toAidl(handle, true /* doDup */);
+}
+
+}  // namespace android
diff --git a/common/support/include/aidlcommonsupport/NativeHandle.h b/common/support/include/aidlcommonsupport/NativeHandle.h
new file mode 100644
index 0000000..10eecba
--- /dev/null
+++ b/common/support/include/aidlcommonsupport/NativeHandle.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/common/NativeHandle.h>
+#include <cutils/native_handle.h>
+
+namespace android {
+
+/**
+ * Creates a libcutils native handle from an AIDL native handle, but it does not
+ * dup internally, so it will contain the same FDs as the handle itself. The
+ * result should be deleted with native_handle_delete.
+ */
+native_handle_t* makeFromAidl(const aidl::android::hardware::common::NativeHandle& handle);
+
+/**
+ * Creates a libcutils native handle from an AIDL native handle with a dup
+ * internally. It's expected the handle is cleaned up with native_handle_close
+ * and native_handle_delete.
+ */
+native_handle_t* dupFromAidl(const aidl::android::hardware::common::NativeHandle& handle);
+
+/**
+ * Creates an AIDL native handle from a libcutils native handle, but does not
+ * dup internally, so the result will contain the same FDs as the handle itself.
+ *
+ * Warning: this passes ownership of the FDs to the ScopedFileDescriptor
+ * objects.
+ */
+aidl::android::hardware::common::NativeHandle makeToAidl(const native_handle_t* handle);
+
+/**
+ * Creates an AIDL native handle from a libcutils native handle with a dup
+ * internally.
+ */
+aidl::android::hardware::common::NativeHandle dupToAidl(const native_handle_t* handle);
+
+}  // namespace android
diff --git a/common/support/test.cpp b/common/support/test.cpp
new file mode 100644
index 0000000..2359277
--- /dev/null
+++ b/common/support/test.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <aidlcommonsupport/NativeHandle.h>
+#include <gtest/gtest.h>
+
+namespace android {
+
+using aidl::android::hardware::common::NativeHandle;
+using ndk::ScopedFileDescriptor;
+
+static void checkEq(const NativeHandle& aidl, native_handle_t* libcutils, bool exceptFds) {
+    ASSERT_NE(libcutils, nullptr);
+    ASSERT_EQ(libcutils->numFds, aidl.fds.size());
+
+    for (size_t i = 0; i < libcutils->numFds; i++) {
+        int afd = aidl.fds.at(i).get();
+        int lfd = libcutils->data[i];
+
+        EXPECT_GE(afd, 0) << "Invalid fd at index " << i;
+        EXPECT_GE(lfd, 0) << "Invalid fd at index " << i;
+
+        if (exceptFds) {
+            EXPECT_NE(afd, lfd) << "Index matched at " << i << " but should be dup'd fd";
+        } else {
+            EXPECT_EQ(afd, lfd) << "Index mismatched at " << i << " but should be same fd";
+        }
+    }
+
+    ASSERT_EQ(libcutils->numInts, aidl.ints.size());
+
+    for (size_t i = 0; i < libcutils->numInts; i++) {
+        int afd = aidl.ints.at(i);
+        int lfd = libcutils->data[libcutils->numFds + i];
+
+        EXPECT_EQ(afd, lfd) << "Index mismatch at " << i;
+    }
+}
+
+static NativeHandle makeTestAidlHandle() {
+    NativeHandle handle = {
+            .fds = std::vector<ScopedFileDescriptor>(2),
+            .ints = {1, 2, 3, 4},
+    };
+    handle.fds[0].set(dup(0));
+    handle.fds[1].set(dup(0));
+    return handle;
+}
+
+TEST(ConvertNativeHandle, MakeFromAidlEmpty) {
+    NativeHandle handle;
+    native_handle_t* to = makeFromAidl(handle);
+    checkEq(handle, to, false /*exceptFds*/);
+    // no native_handle_close b/c fds are owned by NativeHandle
+    EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, MakeFromAidl) {
+    NativeHandle handle = makeTestAidlHandle();
+    native_handle_t* to = makeFromAidl(handle);
+    checkEq(handle, to, false /*exceptFds*/);
+    // no native_handle_close b/c fds are owned by NativeHandle
+    EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, DupFromAidlEmpty) {
+    NativeHandle handle;
+    native_handle_t* to = dupFromAidl(handle);
+    checkEq(handle, to, true /*exceptFds*/);
+    EXPECT_EQ(0, native_handle_close(to));
+    EXPECT_EQ(0, native_handle_delete(to));
+}
+
+TEST(ConvertNativeHandle, DupFromAidl) {
+    NativeHandle handle = makeTestAidlHandle();
+    native_handle_t* to = dupFromAidl(handle);
+    checkEq(handle, to, true /*exceptFds*/);
+    EXPECT_EQ(0, native_handle_close(to));
+    EXPECT_EQ(0, native_handle_delete(to));
+}
+
+static native_handle_t* makeTestLibcutilsHandle() {
+    native_handle_t* handle = native_handle_create(2, 4);
+    handle->data[0] = dup(0);
+    handle->data[1] = dup(0);
+    handle->data[2] = 1;
+    handle->data[3] = 2;
+    handle->data[4] = 3;
+    handle->data[5] = 4;
+    return handle;
+}
+
+TEST(ConvertNativeHandle, MakeToAidlEmpty) {
+    native_handle_t* handle = native_handle_create(0, 0);
+    NativeHandle to = makeToAidl(handle);
+    checkEq(to, handle, false /*exceptFds*/);
+    // no native_handle_close b/c fds are owned by NativeHandle now
+    EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, MakeToAidl) {
+    native_handle_t* handle = makeTestLibcutilsHandle();
+    NativeHandle to = makeToAidl(handle);
+    checkEq(to, handle, false /*exceptFds*/);
+    // no native_handle_close b/c fds are owned by NativeHandle now
+    EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, DupToAidlEmpty) {
+    native_handle_t* handle = native_handle_create(0, 0);
+    NativeHandle to = dupToAidl(handle);
+    checkEq(to, handle, true /*exceptFds*/);
+    EXPECT_EQ(0, native_handle_close(handle));
+    EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+TEST(ConvertNativeHandle, DupToAidl) {
+    native_handle_t* handle = makeTestLibcutilsHandle();
+    NativeHandle to = dupToAidl(handle);
+    checkEq(to, handle, true /*exceptFds*/);
+    EXPECT_EQ(0, native_handle_close(handle));
+    EXPECT_EQ(0, native_handle_delete(handle));
+}
+
+}  // namespace android
diff --git a/compatibility_matrices/Android.bp b/compatibility_matrices/Android.bp
index ba56832..da55347 100644
--- a/compatibility_matrices/Android.bp
+++ b/compatibility_matrices/Android.bp
@@ -12,44 +12,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-vintf_compatibility_matrix {
-    name: "framework_compatibility_matrix.legacy.xml",
-    stem: "compatibility_matrix.legacy.xml",
-    srcs: [
-        "compatibility_matrix.legacy.xml",
-    ],
-    kernel_configs: [
-        // legacy uses O kernel requirements
-        "kernel_config_o_3.18",
-        "kernel_config_o_4.4",
-        "kernel_config_o_4.9",
-    ],
-}
-
-vintf_compatibility_matrix {
-    name: "framework_compatibility_matrix.1.xml",
-    stem: "compatibility_matrix.1.xml",
-    srcs: [
-        "compatibility_matrix.1.xml",
-    ],
-    kernel_configs: [
-        "kernel_config_o_3.18",
-        "kernel_config_o_4.4",
-        "kernel_config_o_4.9",
-    ],
-}
-
-vintf_compatibility_matrix {
-    name: "framework_compatibility_matrix.2.xml",
-    stem: "compatibility_matrix.2.xml",
-    srcs: [
-        "compatibility_matrix.2.xml",
-    ],
-    kernel_configs: [
-        "kernel_config_o_mr1_3.18",
-        "kernel_config_o_mr1_4.4",
-        "kernel_config_o_mr1_4.9",
-    ],
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
 }
 
 vintf_compatibility_matrix {
@@ -90,3 +59,29 @@
         "kernel_config_r_5.4",
     ],
 }
+
+vintf_compatibility_matrix {
+    name: "framework_compatibility_matrix.6.xml",
+    stem: "compatibility_matrix.6.xml",
+    srcs: [
+        "compatibility_matrix.6.xml",
+    ],
+    kernel_configs: [
+        "kernel_config_s_4.19",
+        "kernel_config_s_5.4",
+        "kernel_config_s_5.10",
+    ],
+}
+
+vintf_compatibility_matrix {
+    name: "framework_compatibility_matrix.current.xml",
+    stem: "compatibility_matrix.current.xml",
+    srcs: [
+        "compatibility_matrix.current.xml",
+    ],
+    kernel_configs: [
+        "kernel_config_current_4.19",
+        "kernel_config_current_5.4",
+        "kernel_config_current_5.10",
+    ],
+}
diff --git a/compatibility_matrices/Android.mk b/compatibility_matrices/Android.mk
index e69fc8d..9e715bf 100644
--- a/compatibility_matrices/Android.mk
+++ b/compatibility_matrices/Android.mk
@@ -24,6 +24,9 @@
 include $(CLEAR_VARS)
 include $(LOCAL_PATH)/clear_vars.mk
 LOCAL_MODULE := framework_compatibility_matrix.device.xml
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
 LOCAL_MODULE_STEM := compatibility_matrix.device.xml
 # define LOCAL_MODULE_CLASS for local-generated-sources-dir.
 LOCAL_MODULE_CLASS := ETC
@@ -33,7 +36,7 @@
 LOCAL_SRC_FILES := compatibility_matrix.empty.xml
 else
 
-# DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE specify an absolute path
+# DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE specifies absolute paths
 LOCAL_GENERATED_SOURCES := $(DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE)
 
 # Enforce that DEVICE_FRAMEWORK_COMPATIBILITY_MATRIX_FILE does not specify required HALs
@@ -66,6 +69,9 @@
 include $(CLEAR_VARS)
 include $(LOCAL_PATH)/clear_vars.mk
 LOCAL_MODULE := product_compatibility_matrix.xml
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
 
 ifndef DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE
 my_framework_matrix_deps :=
@@ -77,7 +83,7 @@
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_RELATIVE_PATH := vintf
 
-# DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE specify an absolute path
+# DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE specifies absolute paths
 LOCAL_GENERATED_SOURCES := $(DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE)
 
 # Enforce that DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE does not specify required HALs
@@ -92,12 +98,11 @@
 endif # DEVICE_PRODUCT_COMPATIBILITY_MATRIX_FILE
 
 my_system_matrix_deps := \
-    framework_compatibility_matrix.legacy.xml \
-    framework_compatibility_matrix.1.xml \
-    framework_compatibility_matrix.2.xml \
     framework_compatibility_matrix.3.xml \
     framework_compatibility_matrix.4.xml \
     framework_compatibility_matrix.5.xml \
+    framework_compatibility_matrix.6.xml \
+    framework_compatibility_matrix.current.xml \
     framework_compatibility_matrix.device.xml \
 
 my_framework_matrix_deps += \
@@ -106,12 +111,18 @@
 # Phony target that installs all system compatibility matrix files
 include $(CLEAR_VARS)
 LOCAL_MODULE := system_compatibility_matrix.xml
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
 LOCAL_REQUIRED_MODULES := $(my_system_matrix_deps)
 include $(BUILD_PHONY_PACKAGE)
 
 # Phony target that installs all framework compatibility matrix files (system + product)
 include $(CLEAR_VARS)
 LOCAL_MODULE := framework_compatibility_matrix.xml
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../NOTICE
 LOCAL_REQUIRED_MODULES := $(my_framework_matrix_deps)
 include $(BUILD_PHONY_PACKAGE)
 
diff --git a/compatibility_matrices/CleanSpec.mk b/compatibility_matrices/CleanSpec.mk
index 9b150ed..5b351a5 100644
--- a/compatibility_matrices/CleanSpec.mk
+++ b/compatibility_matrices/CleanSpec.mk
@@ -45,3 +45,6 @@
 #$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
 
 $(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vintf/compatibility_matrix.device.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vintf/compatibility_matrix.legacy.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vintf/compatibility_matrix.1.xml)
+$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/etc/vintf/compatibility_matrix.2.xml)
diff --git a/compatibility_matrices/build/Android.bp b/compatibility_matrices/build/Android.bp
index 42dc886..79ef36d 100644
--- a/compatibility_matrices/build/Android.bp
+++ b/compatibility_matrices/build/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 bootstrap_go_package {
     name: "vintf-compatibility-matrix-soong-rules",
     pkgPath: "android/soong/vintf-compatibility-matrix",
diff --git a/compatibility_matrices/build/vintf_compatibility_matrix.go b/compatibility_matrices/build/vintf_compatibility_matrix.go
index 2772ba3..c72cbde 100644
--- a/compatibility_matrices/build/vintf_compatibility_matrix.go
+++ b/compatibility_matrices/build/vintf_compatibility_matrix.go
@@ -41,7 +41,7 @@
 	}, "inputs")
 
 	xmllintXsd = pctx.AndroidStaticRule("xmllint-xsd", blueprint.RuleParams{
-		Command:     `$XmlLintCmd --schema $xsd $in > /dev/null && touch -a $out`,
+		Command:     `$XmlLintCmd --quiet --schema $xsd $in > /dev/null && touch -a $out`,
 		CommandDeps: []string{"$XmlLintCmd"},
 		Restat:      true,
 	}, "xsd")
@@ -153,7 +153,7 @@
 		if k, ok := m.(*configs.KernelConfigRule); ok {
 			inputPaths = append(inputPaths, k.OutputPath())
 		} else {
-			ctx.PropertyErrorf("kernel_config",
+			ctx.PropertyErrorf("kernel_configs",
 				"module %q is not a kernel_config", ctx.OtherModuleName(m))
 		}
 	})
diff --git a/compatibility_matrices/compatibility_matrix.1.xml b/compatibility_matrices/compatibility_matrix.1.xml
deleted file mode 100644
index cccf24f..0000000
--- a/compatibility_matrices/compatibility_matrix.1.xml
+++ /dev/null
@@ -1,326 +0,0 @@
-<compatibility-matrix version="1.0" type="framework" level="1">
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio</name>
-        <version>2.0</version>
-        <interface>
-            <name>IDevicesFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio.effect</name>
-        <version>2.0</version>
-        <interface>
-            <name>IEffectsFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.evs</name>
-        <version>1.0</version>
-        <interface>
-            <name>IEvsEnumerator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.vehicle</name>
-        <version>2.0</version>
-        <interface>
-            <name>IVehicle</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.biometrics.fingerprint</name>
-        <version>2.1</version>
-        <interface>
-            <name>IBiometricsFingerprint</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.bluetooth</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBluetoothHci</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.boot</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBootControl</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.broadcastradio</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBroadcastRadioFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.camera.provider</name>
-        <version>2.4</version>
-        <interface>
-            <name>ICameraProvider</name>
-            <instance>legacy/0</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.configstore</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISurfaceFlingerConfigs</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.contexthub</name>
-        <version>1.0</version>
-        <interface>
-            <name>IContexthub</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.drm</name>
-        <version>1.0</version>
-        <interface>
-            <name>ICryptoFactory</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IDrmFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.dumpstate</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDumpstateDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.gatekeeper</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGatekeeper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.gnss</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGnss</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.allocator</name>
-        <version>2.0</version>
-        <interface>
-            <name>IAllocator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.composer</name>
-        <version>2.1</version>
-        <interface>
-            <name>IComposer</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.mapper</name>
-        <version>2.0</version>
-        <interface>
-            <name>IMapper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.health</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHealth</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.ir</name>
-        <version>1.0</version>
-        <interface>
-            <name>IConsumerIr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.keymaster</name>
-        <version>3.0</version>
-        <interface>
-            <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.light</name>
-        <version>2.0</version>
-        <interface>
-            <name>ILight</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.media.omx</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOmx</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IOmxStore</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.memtrack</name>
-        <version>1.0</version>
-        <interface>
-            <name>IMemtrack</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.nfc</name>
-        <version>1.0</version>
-        <interface>
-            <name>INfc</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.power</name>
-        <version>1.0</version>
-        <interface>
-            <name>IPower</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio</name>
-        <version>1.0</version>
-        <interface>
-            <name>IRadio</name>
-            <instance>slot1</instance>
-        </interface>
-        <interface>
-            <name>ISap</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio.deprecated</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOemHook</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.renderscript</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.sensors</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISensors</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.soundtrigger</name>
-        <version>2.0</version>
-        <interface>
-            <name>ISoundTriggerHw</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.thermal</name>
-        <version>1.0</version>
-        <interface>
-            <name>IThermal</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.cec</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHdmiCec</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.input</name>
-        <version>1.0</version>
-        <interface>
-            <name>ITvInput</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.usb</name>
-        <version>1.0</version>
-        <interface>
-            <name>IUsb</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vibrator</name>
-        <version>1.0</version>
-        <interface>
-            <name>IVibrator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vr</name>
-        <version>1.0</version>
-        <interface>
-            <name>IVr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi</name>
-        <version>1.0</version>
-        <interface>
-            <name>IWifi</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi.supplicant</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISupplicant</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</compatibility-matrix>
diff --git a/compatibility_matrices/compatibility_matrix.2.xml b/compatibility_matrices/compatibility_matrix.2.xml
deleted file mode 100644
index d4f9809..0000000
--- a/compatibility_matrices/compatibility_matrix.2.xml
+++ /dev/null
@@ -1,374 +0,0 @@
-<compatibility-matrix version="1.0" type="framework" level="2">
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio</name>
-        <version>2.0</version>
-        <interface>
-            <name>IDevicesFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio.effect</name>
-        <version>2.0</version>
-        <interface>
-            <name>IEffectsFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.evs</name>
-        <version>1.0</version>
-        <interface>
-            <name>IEvsEnumerator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.vehicle</name>
-        <version>2.0</version>
-        <interface>
-            <name>IVehicle</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.biometrics.fingerprint</name>
-        <version>2.1</version>
-        <interface>
-            <name>IBiometricsFingerprint</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.bluetooth</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBluetoothHci</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.boot</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBootControl</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.broadcastradio</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IBroadcastRadioFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.camera.provider</name>
-        <version>2.4</version>
-        <interface>
-            <name>ICameraProvider</name>
-            <instance>legacy/0</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.cas</name>
-        <version>1.0</version>
-        <interface>
-            <name>IMediaCasService</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.configstore</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISurfaceFlingerConfigs</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.contexthub</name>
-        <version>1.0</version>
-        <interface>
-            <name>IContexthub</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.drm</name>
-        <version>1.0</version>
-        <interface>
-            <name>ICryptoFactory</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IDrmFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.dumpstate</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDumpstateDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.gatekeeper</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGatekeeper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.gnss</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGnss</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.allocator</name>
-        <version>2.0</version>
-        <interface>
-            <name>IAllocator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.composer</name>
-        <version>2.1</version>
-        <interface>
-            <name>IComposer</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.mapper</name>
-        <version>2.0</version>
-        <interface>
-            <name>IMapper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.health</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHealth</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.ir</name>
-        <version>1.0</version>
-        <interface>
-            <name>IConsumerIr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.keymaster</name>
-        <version>3.0</version>
-        <interface>
-            <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.light</name>
-        <version>2.0</version>
-        <interface>
-            <name>ILight</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.media.omx</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOmx</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IOmxStore</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.memtrack</name>
-        <version>1.0</version>
-        <interface>
-            <name>IMemtrack</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.nfc</name>
-        <version>1.0</version>
-        <interface>
-            <name>INfc</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.oemlock</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOemLock</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.power</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IPower</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IRadio</name>
-            <instance>slot1</instance>
-        </interface>
-        <interface>
-            <name>ISap</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio.deprecated</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOemHook</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.renderscript</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.sensors</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISensors</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.soundtrigger</name>
-        <version>2.0</version>
-        <interface>
-            <name>ISoundTriggerHw</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tetheroffload.config</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOffloadConfig</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tetheroffload.control</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOffloadControl</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.thermal</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IThermal</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.cec</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHdmiCec</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.input</name>
-        <version>1.0</version>
-        <interface>
-            <name>ITvInput</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.usb</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IUsb</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vibrator</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IVibrator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vr</name>
-        <version>1.0</version>
-        <interface>
-            <name>IVr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.weaver</name>
-        <version>1.0</version>
-        <interface>
-            <name>IWeaver</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi</name>
-        <version>1.0-1</version>
-        <interface>
-            <name>IWifi</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi.offload</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOffload</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi.supplicant</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISupplicant</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</compatibility-matrix>
diff --git a/compatibility_matrices/compatibility_matrix.3.xml b/compatibility_matrices/compatibility_matrix.3.xml
index 5888ab9..a75ed25 100644
--- a/compatibility_matrices/compatibility_matrix.3.xml
+++ b/compatibility_matrices/compatibility_matrix.3.xml
@@ -223,7 +223,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0</version>
@@ -310,6 +310,10 @@
             <instance>slot2</instance>
             <instance>slot3</instance>
         </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.0-2</version>
         <interface>
             <name>ISap</name>
             <instance>slot1</instance>
diff --git a/compatibility_matrices/compatibility_matrix.4.xml b/compatibility_matrices/compatibility_matrix.4.xml
index e5e012c..3b8ee21 100644
--- a/compatibility_matrices/compatibility_matrix.4.xml
+++ b/compatibility_matrices/compatibility_matrix.4.xml
@@ -245,7 +245,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0</version>
diff --git a/compatibility_matrices/compatibility_matrix.5.xml b/compatibility_matrices/compatibility_matrix.5.xml
index e772b6f..0fb21a7 100644
--- a/compatibility_matrices/compatibility_matrix.5.xml
+++ b/compatibility_matrices/compatibility_matrix.5.xml
@@ -86,7 +86,7 @@
     </hal>
     <hal format="hidl" optional="true">
         <name>android.hardware.biometrics.face</name>
-        <version>1.0-1</version>
+        <version>1.0</version>
         <interface>
             <name>IBiometricsFace</name>
             <instance>default</instance>
@@ -256,6 +256,13 @@
     </hal>
     <hal format="aidl" optional="true">
         <name>android.hardware.identity</name>
+        <!--
+          b/178458001: identity V2 is introduced in R, but Android R VINTF does not support AIDL
+          versions. Hence, we only specify identity V2 in compatibility_matrix.5.xml in Android S+
+          branches. In Android R branches, the matrix implicitly specifies V1.
+          SingleManifestTest.ManifestAidlHalsServed has an exemption for this.
+        -->
+        <version>1-2</version>
         <interface>
             <name>IIdentityCredentialStore</name>
             <instance>default</instance>
@@ -277,7 +284,7 @@
             <instance>default</instance>
         </interface>
     </hal>
-    <hal format="hidl" optional="false">
+    <hal format="hidl" optional="true">
         <name>android.hardware.keymaster</name>
         <version>3.0</version>
         <version>4.0-1</version>
diff --git a/compatibility_matrices/compatibility_matrix.6.xml b/compatibility_matrices/compatibility_matrix.6.xml
new file mode 100644
index 0000000..039ea84
--- /dev/null
+++ b/compatibility_matrices/compatibility_matrix.6.xml
@@ -0,0 +1,622 @@
+<compatibility-matrix version="1.0" type="framework" level="6">
+    <hal format="hidl" optional="true">
+        <name>android.hardware.atrace</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAtraceDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio</name>
+        <version>6.0</version>
+        <version>7.0</version>
+        <interface>
+            <name>IDevicesFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio.effect</name>
+        <version>6.0</version>
+        <version>7.0</version>
+        <interface>
+            <name>IEffectsFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+         <name>android.hardware.authsecret</name>
+         <version>1</version>
+         <interface>
+             <name>IAuthSecret</name>
+             <instance>default</instance>
+         </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.authsecret</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAuthSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.audiocontrol</name>
+        <version>1.0</version>
+        <version>2.0</version>
+        <interface>
+            <name>IAudioControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.can</name>
+        <version>1.0</version>
+        <interface>
+            <name>ICanBus</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>ICanController</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.evs</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IEvsEnumerator</name>
+            <instance>default</instance>
+            <regex-instance>[a-z]+/[0-9]+</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.automotive.occupant_awareness</name>
+        <version>1</version>
+        <interface>
+            <name>IOccupantAwareness</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.sv</name>
+        <version>1.0</version>
+        <interface>
+            <name>ISurroundViewService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.vehicle</name>
+        <version>2.0</version>
+        <interface>
+            <name>IVehicle</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.biometrics.face</name>
+        <version>1.0</version>
+        <interface>
+            <name>IBiometricsFace</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <version>2.1-2</version>
+        <interface>
+            <name>IBiometricsFingerprint</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IBluetoothHci</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth.audio</name>
+        <version>2.0-1</version>
+        <interface>
+            <name>IBluetoothAudioProvidersFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.boot</name>
+        <version>1.2</version>
+        <interface>
+            <name>IBootControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IBroadcastRadioFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <version>2.0</version>
+        <interface>
+            <name>IBroadcastRadio</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.camera.provider</name>
+        <version>2.4-6</version>
+        <interface>
+            <name>ICameraProvider</name>
+            <regex-instance>[^/]+/[0-9]+</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.cas</name>
+        <version>1.1-2</version>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.confirmationui</name>
+        <version>1.0</version>
+        <interface>
+            <name>IConfirmationUI</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.contexthub</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IContexthub</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.drm</name>
+        <version>1.3</version>
+        <interface>
+            <name>ICryptoFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>IDrmFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.dumpstate</name>
+        <version>1.1</version>
+        <interface>
+            <name>IDumpstateDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.gatekeeper</name>
+        <version>1.0</version>
+        <interface>
+            <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.gnss</name>
+        <version>2.0-1</version>
+        <interface>
+            <name>IGnss</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.allocator</name>
+        <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
+        <version>2.0</version>
+        <version>3.0</version>
+        <version>4.0</version>
+        <interface>
+            <name>IAllocator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.composer</name>
+        <version>2.1-4</version>
+        <interface>
+            <name>IComposer</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.mapper</name>
+        <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
+        <version>2.1</version>
+        <version>3.0</version>
+        <version>4.0</version>
+        <interface>
+            <name>IMapper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.health</name>
+        <version>2.1</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.health.storage</name>
+        <version>1</version>
+        <interface>
+            <name>IStorage</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.identity</name>
+        <version>1-3</version>
+        <interface>
+            <name>IIdentityCredentialStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.oemlock</name>
+        <version>1</version>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.ir</name>
+        <version>1.0</version>
+        <interface>
+            <name>IConsumerIr</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.input.classifier</name>
+        <version>1.0</version>
+        <interface>
+            <name>IInputClassifier</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>3.0</version>
+        <version>4.0-1</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>4.0-1</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.keymint</name>
+        <version>1</version>
+        <interface>
+            <name>IKeyMintDevice</name>
+            <instance>default</instance>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.keymint</name>
+        <interface>
+            <name>IRemotelyProvisionedComponent</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.light</name>
+        <version>1</version>
+        <interface>
+            <name>ILights</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.media.c2</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IComponentStore</name>
+            <regex-instance>default[0-9]*</regex-instance>
+            <regex-instance>vendor[0-9]*_software</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.media.omx</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOmx</name>
+            <instance>default</instance>
+        </interface>
+        <interface>
+            <name>IOmxStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.memtrack</name>
+        <version>1</version>
+        <interface>
+            <name>IMemtrack</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <version>1.0-3</version>
+        <interface>
+            <name>IDevice</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <interface>
+            <name>IDevice</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.nfc</name>
+        <version>1.2</version>
+        <interface>
+            <name>INfc</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.oemlock</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="false">
+        <name>android.hardware.power</name>
+        <version>1</version>
+        <interface>
+            <name>IPower</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.power.stats</name>
+        <interface>
+            <name>IPowerStats</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.6</version>
+        <interface>
+            <name>IRadio</name>
+            <instance>slot1</instance>
+            <instance>slot2</instance>
+            <instance>slot3</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.2</version>
+        <interface>
+            <name>ISap</name>
+            <instance>slot1</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <!--
+        See compatibility_matrix.4.xml on versioning of radio config HAL.
+        -->
+        <version>1.1</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <version>1.3</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.renderscript</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.rebootescrow</name>
+        <version>1</version>
+        <interface>
+            <name>IRebootEscrow</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.secure_element</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>ISecureElement</name>
+            <regex-instance>eSE[1-9][0-9]*</regex-instance>
+            <regex-instance>SIM[1-9][0-9]*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.secureclock</name>
+        <version>1</version>
+        <interface>
+            <name>ISecureClock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.sharedsecret</name>
+        <version>1</version>
+        <interface>
+            <name>ISharedSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.sensors</name>
+        <version>1.0</version>
+        <version>2.0-1</version>
+        <interface>
+            <name>ISensors</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.soundtrigger</name>
+        <version>2.0-3</version>
+        <interface>
+            <name>ISoundTriggerHw</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.config</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOffloadConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.control</name>
+        <version>1.1</version>
+        <interface>
+            <name>IOffloadControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.thermal</name>
+        <version>2.0</version>
+        <interface>
+            <name>IThermal</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.cec</name>
+        <version>1.0</version>
+        <interface>
+            <name>IHdmiCec</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.input</name>
+        <version>1.0</version>
+        <interface>
+            <name>ITvInput</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.tuner</name>
+        <version>1.0</version>
+        <interface>
+            <name>ITuner</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IUsb</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb.gadget</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.vibrator</name>
+        <interface>
+            <name>IVibrator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.weaver</name>
+        <version>1.0</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.weaver</name>
+        <version>1</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi</name>
+        <version>1.3-4</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.hostapd</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.supplicant</name>
+        <version>1.2-3</version>
+        <interface>
+            <name>ISupplicant</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</compatibility-matrix>
diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml
new file mode 100644
index 0000000..0b779ee
--- /dev/null
+++ b/compatibility_matrices/compatibility_matrix.current.xml
@@ -0,0 +1,622 @@
+<compatibility-matrix version="1.0" type="framework" level="7">
+    <hal format="hidl" optional="true">
+        <name>android.hardware.atrace</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAtraceDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio</name>
+        <version>6.0</version>
+        <version>7.0</version>
+        <interface>
+            <name>IDevicesFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.audio.effect</name>
+        <version>6.0</version>
+        <version>7.0</version>
+        <interface>
+            <name>IEffectsFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+         <name>android.hardware.authsecret</name>
+         <version>1</version>
+         <interface>
+             <name>IAuthSecret</name>
+             <instance>default</instance>
+         </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.authsecret</name>
+        <version>1.0</version>
+        <interface>
+            <name>IAuthSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.audiocontrol</name>
+        <version>1.0</version>
+        <version>2.0</version>
+        <interface>
+            <name>IAudioControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.can</name>
+        <version>1.0</version>
+        <interface>
+            <name>ICanBus</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>ICanController</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.evs</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IEvsEnumerator</name>
+            <instance>default</instance>
+            <regex-instance>[a-z]+/[0-9]+</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.automotive.occupant_awareness</name>
+        <version>1</version>
+        <interface>
+            <name>IOccupantAwareness</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.sv</name>
+        <version>1.0</version>
+        <interface>
+            <name>ISurroundViewService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.automotive.vehicle</name>
+        <version>2.0</version>
+        <interface>
+            <name>IVehicle</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.biometrics.face</name>
+        <version>1.0</version>
+        <interface>
+            <name>IBiometricsFace</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.biometrics.fingerprint</name>
+        <version>2.1-2</version>
+        <interface>
+            <name>IBiometricsFingerprint</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IBluetoothHci</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.bluetooth.audio</name>
+        <version>2.0-1</version>
+        <interface>
+            <name>IBluetoothAudioProvidersFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.boot</name>
+        <version>1.2</version>
+        <interface>
+            <name>IBootControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IBroadcastRadioFactory</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.broadcastradio</name>
+        <version>2.0</version>
+        <interface>
+            <name>IBroadcastRadio</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.camera.provider</name>
+        <version>2.4-6</version>
+        <interface>
+            <name>ICameraProvider</name>
+            <regex-instance>[^/]+/[0-9]+</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.cas</name>
+        <version>1.1-2</version>
+        <interface>
+            <name>IMediaCasService</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.confirmationui</name>
+        <version>1.0</version>
+        <interface>
+            <name>IConfirmationUI</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.contexthub</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IContexthub</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.drm</name>
+        <version>1.3</version>
+        <interface>
+            <name>ICryptoFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+        <interface>
+            <name>IDrmFactory</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.dumpstate</name>
+        <version>1.1</version>
+        <interface>
+            <name>IDumpstateDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.gatekeeper</name>
+        <version>1.0</version>
+        <interface>
+            <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.gnss</name>
+        <version>2.0-1</version>
+        <interface>
+            <name>IGnss</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.allocator</name>
+        <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
+        <version>2.0</version>
+        <version>3.0</version>
+        <version>4.0</version>
+        <interface>
+            <name>IAllocator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.composer</name>
+        <version>2.1-4</version>
+        <interface>
+            <name>IComposer</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.graphics.mapper</name>
+        <!-- New, non-Go devices should use 4.0, tested in vts_treble_vintf_vendor_test -->
+        <version>2.1</version>
+        <version>3.0</version>
+        <version>4.0</version>
+        <interface>
+            <name>IMapper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="false">
+        <name>android.hardware.health</name>
+        <version>2.1</version>
+        <interface>
+            <name>IHealth</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.health.storage</name>
+        <version>1</version>
+        <interface>
+            <name>IStorage</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.identity</name>
+        <version>1-3</version>
+        <interface>
+            <name>IIdentityCredentialStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.oemlock</name>
+        <version>1</version>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.ir</name>
+        <version>1.0</version>
+        <interface>
+            <name>IConsumerIr</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.input.classifier</name>
+        <version>1.0</version>
+        <interface>
+            <name>IInputClassifier</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>3.0</version>
+        <version>4.0-1</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.keymaster</name>
+        <version>4.0-1</version>
+        <interface>
+            <name>IKeymasterDevice</name>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.keymint</name>
+        <version>1</version>
+        <interface>
+            <name>IKeyMintDevice</name>
+            <instance>default</instance>
+            <instance>strongbox</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.keymint</name>
+        <interface>
+            <name>IRemotelyProvisionedComponent</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.light</name>
+        <version>1</version>
+        <interface>
+            <name>ILights</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.media.c2</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IComponentStore</name>
+            <regex-instance>default[0-9]*</regex-instance>
+            <regex-instance>vendor[0-9]*_software</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.media.omx</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOmx</name>
+            <instance>default</instance>
+        </interface>
+        <interface>
+            <name>IOmxStore</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.memtrack</name>
+        <version>1</version>
+        <interface>
+            <name>IMemtrack</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <version>1.0-3</version>
+        <interface>
+            <name>IDevice</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.neuralnetworks</name>
+        <interface>
+            <name>IDevice</name>
+            <regex-instance>.*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.nfc</name>
+        <version>1.2</version>
+        <interface>
+            <name>INfc</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.oemlock</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="false">
+        <name>android.hardware.power</name>
+        <version>1</version>
+        <interface>
+            <name>IPower</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.power.stats</name>
+        <interface>
+            <name>IPowerStats</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.6</version>
+        <interface>
+            <name>IRadio</name>
+            <instance>slot1</instance>
+            <instance>slot2</instance>
+            <instance>slot3</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio</name>
+        <version>1.2</version>
+        <interface>
+            <name>ISap</name>
+            <instance>slot1</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <!--
+        See compatibility_matrix.4.xml on versioning of radio config HAL.
+        -->
+        <version>1.1</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.radio.config</name>
+        <version>1.3</version>
+        <interface>
+            <name>IRadioConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.renderscript</name>
+        <version>1.0</version>
+        <interface>
+            <name>IDevice</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.rebootescrow</name>
+        <version>1</version>
+        <interface>
+            <name>IRebootEscrow</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.secure_element</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>ISecureElement</name>
+            <regex-instance>eSE[1-9][0-9]*</regex-instance>
+            <regex-instance>SIM[1-9][0-9]*</regex-instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.secureclock</name>
+        <version>1</version>
+        <interface>
+            <name>ISecureClock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.security.sharedsecret</name>
+        <version>1</version>
+        <interface>
+            <name>ISharedSecret</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.sensors</name>
+        <version>1.0</version>
+        <version>2.0-1</version>
+        <interface>
+            <name>ISensors</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.soundtrigger</name>
+        <version>2.0-3</version>
+        <interface>
+            <name>ISoundTriggerHw</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.config</name>
+        <version>1.0</version>
+        <interface>
+            <name>IOffloadConfig</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tetheroffload.control</name>
+        <version>1.1</version>
+        <interface>
+            <name>IOffloadControl</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.thermal</name>
+        <version>2.0</version>
+        <interface>
+            <name>IThermal</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.cec</name>
+        <version>1.0</version>
+        <interface>
+            <name>IHdmiCec</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.input</name>
+        <version>1.0</version>
+        <interface>
+            <name>ITvInput</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.tv.tuner</name>
+        <version>1.0</version>
+        <interface>
+            <name>ITuner</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IUsb</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.usb.gadget</name>
+        <version>1.0-1</version>
+        <interface>
+            <name>IUsbGadget</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.vibrator</name>
+        <interface>
+            <name>IVibrator</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.weaver</name>
+        <version>1.0</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="aidl" optional="true">
+        <name>android.hardware.weaver</name>
+        <version>1</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi</name>
+        <version>1.3-4</version>
+        <interface>
+            <name>IWifi</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.hostapd</name>
+        <version>1.0-2</version>
+        <interface>
+            <name>IHostapd</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+    <hal format="hidl" optional="true">
+        <name>android.hardware.wifi.supplicant</name>
+        <version>1.2-3</version>
+        <interface>
+            <name>ISupplicant</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</compatibility-matrix>
diff --git a/compatibility_matrices/compatibility_matrix.legacy.xml b/compatibility_matrices/compatibility_matrix.legacy.xml
deleted file mode 100644
index 8a4d2ee..0000000
--- a/compatibility_matrices/compatibility_matrix.legacy.xml
+++ /dev/null
@@ -1,326 +0,0 @@
-<compatibility-matrix version="1.0" type="framework" level="legacy">
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio</name>
-        <version>2.0</version>
-        <interface>
-            <name>IDevicesFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.audio.effect</name>
-        <version>2.0</version>
-        <interface>
-            <name>IEffectsFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.evs</name>
-        <version>1.0</version>
-        <interface>
-            <name>IEvsEnumerator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.automotive.vehicle</name>
-        <version>2.0</version>
-        <interface>
-            <name>IVehicle</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.biometrics.fingerprint</name>
-        <version>2.1</version>
-        <interface>
-            <name>IBiometricsFingerprint</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.bluetooth</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBluetoothHci</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.boot</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBootControl</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.broadcastradio</name>
-        <version>1.0</version>
-        <interface>
-            <name>IBroadcastRadioFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.camera.provider</name>
-        <version>2.4</version>
-        <interface>
-            <name>ICameraProvider</name>
-            <instance>legacy/0</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.configstore</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISurfaceFlingerConfigs</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.contexthub</name>
-        <version>1.0</version>
-        <interface>
-            <name>IContexthub</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.drm</name>
-        <version>1.0</version>
-        <interface>
-            <name>ICryptoFactory</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IDrmFactory</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.dumpstate</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDumpstateDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.gatekeeper</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGatekeeper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.gnss</name>
-        <version>1.0</version>
-        <interface>
-            <name>IGnss</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.allocator</name>
-        <version>2.0</version>
-        <interface>
-            <name>IAllocator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.composer</name>
-        <version>2.1</version>
-        <interface>
-            <name>IComposer</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.graphics.mapper</name>
-        <version>2.0</version>
-        <interface>
-            <name>IMapper</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.health</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHealth</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.ir</name>
-        <version>1.0</version>
-        <interface>
-            <name>IConsumerIr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.keymaster</name>
-        <version>3.0</version>
-        <interface>
-            <name>IKeymasterDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.light</name>
-        <version>2.0</version>
-        <interface>
-            <name>ILight</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="false">
-        <name>android.hardware.media.omx</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOmx</name>
-            <instance>default</instance>
-        </interface>
-        <interface>
-            <name>IOmxStore</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.memtrack</name>
-        <version>1.0</version>
-        <interface>
-            <name>IMemtrack</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.nfc</name>
-        <version>1.0</version>
-        <interface>
-            <name>INfc</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.power</name>
-        <version>1.0</version>
-        <interface>
-            <name>IPower</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio</name>
-        <version>1.0</version>
-        <interface>
-            <name>IRadio</name>
-            <instance>slot1</instance>
-        </interface>
-        <interface>
-            <name>ISap</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.radio.deprecated</name>
-        <version>1.0</version>
-        <interface>
-            <name>IOemHook</name>
-            <instance>slot1</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.renderscript</name>
-        <version>1.0</version>
-        <interface>
-            <name>IDevice</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.sensors</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISensors</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.soundtrigger</name>
-        <version>2.0</version>
-        <interface>
-            <name>ISoundTriggerHw</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.thermal</name>
-        <version>1.0</version>
-        <interface>
-            <name>IThermal</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.cec</name>
-        <version>1.0</version>
-        <interface>
-            <name>IHdmiCec</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.tv.input</name>
-        <version>1.0</version>
-        <interface>
-            <name>ITvInput</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.usb</name>
-        <version>1.0</version>
-        <interface>
-            <name>IUsb</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vibrator</name>
-        <version>1.0</version>
-        <interface>
-            <name>IVibrator</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.vr</name>
-        <version>1.0</version>
-        <interface>
-            <name>IVr</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi</name>
-        <version>1.0</version>
-        <interface>
-            <name>IWifi</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    <hal format="hidl" optional="true">
-        <name>android.hardware.wifi.supplicant</name>
-        <version>1.0</version>
-        <interface>
-            <name>ISupplicant</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</compatibility-matrix>
diff --git a/compatibility_matrices/exclude/Android.bp b/compatibility_matrices/exclude/Android.bp
new file mode 100644
index 0000000..9b686ae
--- /dev/null
+++ b/compatibility_matrices/exclude/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_host_static {
+    name: "libvintf_fcm_exclude",
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libbase",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    srcs: [
+        "fcm_exclude.cpp",
+    ],
+}
diff --git a/compatibility_matrices/exclude/fcm_exclude.cpp b/compatibility_matrices/exclude/fcm_exclude.cpp
new file mode 100644
index 0000000..6a4d251
--- /dev/null
+++ b/compatibility_matrices/exclude/fcm_exclude.cpp
@@ -0,0 +1,93 @@
+/*
+ * 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 <string>
+#include <vector>
+
+#include <android-base/strings.h>
+#include <vintf/fcm_exclude.h>
+
+namespace android::vintf::details {
+
+// The predicate to VintfObject::checkMissingHalsInMatrices.
+bool ShouldCheckMissingHalsInFcm(const std::string& package) {
+    using std::placeholders::_1;
+
+    static std::vector<std::string> included_prefixes{
+            // Other AOSP HALs (e.g. android.frameworks.*) are not added because only framework
+            // matrix is checked.
+            "android.hardware.",
+    };
+
+    static std::vector<std::string> excluded_prefixes{
+            // Packages without top level interfaces (including types-only packages) are exempted.
+            "android.hardware.camera.device@",
+            "android.hardware.gnss.measurement_corrections@1.",
+            "android.hardware.graphics.bufferqueue@",
+
+            // Test packages are exempted.
+            "android.hardware.tests.",
+    };
+
+    static std::vector<std::string> excluded_exact{
+            // Packages without top level interfaces (including types-only packages) are exempted.
+            // HIDL
+            "android.hardware.cas.native@1.0",
+            "android.hardware.gnss.visibility_control@1.0",
+            "android.hardware.media.bufferpool@1.0",
+            "android.hardware.media.bufferpool@2.0",
+            "android.hardware.radio.config@1.2",
+            // AIDL
+            "android.hardware.common",
+            "android.hardware.common.fmq",
+            "android.hardware.graphics.common",
+            "android.hardware.keymaster",
+
+            // Fastboot HAL is only used by recovery. Recovery is owned by OEM. Framework
+            // does not depend on this HAL, hence it is not declared in any manifests or matrices.
+            "android.hardware.fastboot@1.0",
+            "android.hardware.fastboot@1.1",
+
+            // Deprecated HALs.
+            // HIDL
+            // TODO(b/171260360) Remove when HAL definition is removed
+            "android.hardware.audio.effect@2.0",
+            "android.hardware.audio@2.0",
+            // Health 1.0 HAL is deprecated. The top level interface are deleted.
+            "android.hardware.health@1.0",
+            // TODO(b/171260670) Remove when HAL definition is removed
+            "android.hardware.nfc@1.0",
+            // TODO(b/171260715) Remove when HAL definition is removed
+            "android.hardware.radio.deprecated@1.0",
+    };
+
+    auto package_has_prefix = [&](const std::string& prefix) {
+        return android::base::StartsWith(package, prefix);
+    };
+
+    // Only check packageAndVersions that are in the include list and not in the exclude list.
+    if (!std::any_of(included_prefixes.begin(), included_prefixes.end(), package_has_prefix)) {
+        return false;
+    }
+
+    if (std::find(excluded_exact.begin(), excluded_exact.end(), package) != excluded_exact.end()) {
+        return false;
+    }
+
+    return !std::any_of(excluded_prefixes.begin(), excluded_prefixes.end(), package_has_prefix);
+}
+
+}  // namespace android::vintf::details
diff --git a/compatibility_matrices/exclude/include/vintf/fcm_exclude.h b/compatibility_matrices/exclude/include/vintf/fcm_exclude.h
new file mode 100644
index 0000000..f74c217
--- /dev/null
+++ b/compatibility_matrices/exclude/include/vintf/fcm_exclude.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <string>
+#include <vector>
+
+namespace android::vintf::details {
+
+// Determine whether VINTF checks |package| is missing from FCMs.
+// |package| can be a HIDL package and version like
+// "android.hardware.foo@1.0", or an AIDL package name like
+// "android.hardware.foo".
+bool ShouldCheckMissingHalsInFcm(const std::string& package);
+
+}  // namespace android::vintf::details
diff --git a/configstore/1.0/Android.bp b/configstore/1.0/Android.bp
index d92f252..51fc44c 100644
--- a/configstore/1.0/Android.bp
+++ b/configstore/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.configstore@1.0",
     root: "android.hardware",
diff --git a/configstore/1.0/vts/functional/Android.bp b/configstore/1.0/vts/functional/Android.bp
index 4e1e045..27c66fd 100644
--- a/configstore/1.0/vts/functional/Android.bp
+++ b/configstore/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalConfigstoreV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.configstore@1.0"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp b/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
index 8a1a313..34a40cf 100644
--- a/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
+++ b/configstore/1.0/vts/functional/VtsHalConfigstoreV1_0TargetTest.cpp
@@ -140,6 +140,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ConfigstoreHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ConfigstoreHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISurfaceFlingerConfigs::descriptor)),
diff --git a/configstore/1.1/Android.bp b/configstore/1.1/Android.bp
index 7c5f3f7..ec745dd 100644
--- a/configstore/1.1/Android.bp
+++ b/configstore/1.1/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.configstore@1.1",
     root: "android.hardware",
diff --git a/configstore/1.1/default/Android.mk b/configstore/1.1/default/Android.mk
index 6b7bb00..9a6f0fc 100644
--- a/configstore/1.1/default/Android.mk
+++ b/configstore/1.1/default/Android.mk
@@ -3,6 +3,9 @@
 ################################################################################
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.configstore@1.1-service
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 # seccomp is not required for coverage build.
 ifneq ($(NATIVE_COVERAGE),true)
 LOCAL_REQUIRED_MODULES_arm64 := configstore@1.1.policy
@@ -30,6 +33,9 @@
 ifeq ($(TARGET_ARCH), $(filter $(TARGET_ARCH), arm64))
 include $(CLEAR_VARS)
 LOCAL_MODULE := configstore@1.1.policy
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_VENDOR)/etc/seccomp_policy
 LOCAL_SRC_FILES := seccomp_policy/configstore@1.1-$(TARGET_ARCH).policy
@@ -39,6 +45,9 @@
 # disable configstore
 include $(CLEAR_VARS)
 LOCAL_MODULE := disable_configstore
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_MODULE_CLASS := EXECUTABLES
 LOCAL_SRC_FILES:= disable_configstore.cpp
 LOCAL_OVERRIDES_MODULES := android.hardware.configstore@1.1-service
diff --git a/configstore/utils/Android.bp b/configstore/utils/Android.bp
index 178f245..7ed3f68 100644
--- a/configstore/utils/Android.bp
+++ b/configstore/utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.configstore-utils",
     vendor_available: true,
diff --git a/confirmationui/1.0/Android.bp b/confirmationui/1.0/Android.bp
index a22067a..950a55a 100644
--- a/confirmationui/1.0/Android.bp
+++ b/confirmationui/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.confirmationui@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IConfirmationResultCallback.hal",
diff --git a/confirmationui/1.0/default/Android.bp b/confirmationui/1.0/default/Android.bp
index 33a1e07..99f6f76 100644
--- a/confirmationui/1.0/default/Android.bp
+++ b/confirmationui/1.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.confirmationui@1.0-service",
     init_rc: ["android.hardware.confirmationui@1.0-service.rc"],
diff --git a/confirmationui/1.0/vts/functional/Android.bp b/confirmationui/1.0/vts/functional/Android.bp
index c73ee28..6c6488b 100644
--- a/confirmationui/1.0/vts/functional/Android.bp
+++ b/confirmationui/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalConfirmationUIV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
index d953ab0..1b21b0a 100644
--- a/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
+++ b/confirmationui/1.0/vts/functional/VtsHalConfirmationUIV1_0TargetTest.cpp
@@ -473,6 +473,7 @@
     ASSERT_EQ(expected, result.value());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ConfirmationUIHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ConfirmationUIHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IConfirmationUI::descriptor)),
diff --git a/confirmationui/support/Android.bp b/confirmationui/support/Android.bp
index e215baa..6ab83f2 100644
--- a/confirmationui/support/Android.bp
+++ b/confirmationui/support/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.confirmationui-support-lib",
     vendor_available: true,
diff --git a/contexthub/1.0/Android.bp b/contexthub/1.0/Android.bp
index 71dd978..00dc666 100644
--- a/contexthub/1.0/Android.bp
+++ b/contexthub/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.contexthub@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IContexthub.hal",
diff --git a/contexthub/1.0/default/Android.bp b/contexthub/1.0/default/Android.bp
index 8384037..5bd31b6 100644
--- a/contexthub/1.0/default/Android.bp
+++ b/contexthub/1.0/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.contexthub@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/contexthub/1.0/default/Contexthub.cpp b/contexthub/1.0/default/Contexthub.cpp
index 5f83a22..a08c520 100644
--- a/contexthub/1.0/default/Contexthub.cpp
+++ b/contexthub/1.0/default/Contexthub.cpp
@@ -155,10 +155,10 @@
         .message = static_cast<const uint8_t *>(msg.msg.data()),
     };
 
-    // Use a dummy to prevent send_message with empty message from failing prematurely
-    static uint8_t dummy;
+    // Use a placeholder to prevent send_message with empty message from failing prematurely
+    static uint8_t placeholder;
     if (txMsg.message_len == 0 && txMsg.message == nullptr) {
-        txMsg.message = &dummy;
+        txMsg.message = &placeholder;
     }
 
     ALOGI("Sending msg of type %" PRIu32 ", size %" PRIu32 " to app 0x%" PRIx64,
@@ -255,7 +255,7 @@
 
     memcpy(rsp, msg, sizeof(*rsp));
 
-    // No sanity checks on return values
+    // No validations on return values
     return true;
 }
 
diff --git a/contexthub/1.0/vts/functional/Android.bp b/contexthub/1.0/vts/functional/Android.bp
index 091f2dc..5949f8d 100644
--- a/contexthub/1.0/vts/functional/Android.bp
+++ b/contexthub/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalContexthubV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/contexthub/1.1/Android.bp b/contexthub/1.1/Android.bp
index 649f1db..6aba5f7 100644
--- a/contexthub/1.1/Android.bp
+++ b/contexthub/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.contexthub@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IContexthub.hal",
diff --git a/contexthub/1.1/default/Android.bp b/contexthub/1.1/default/Android.bp
index 86858c0..946e3a9 100644
--- a/contexthub/1.1/default/Android.bp
+++ b/contexthub/1.1/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.contexthub@1.1-service.mock",
     defaults: ["hidl_defaults"],
diff --git a/contexthub/1.1/vts/functional/Android.bp b/contexthub/1.1/vts/functional/Android.bp
index 034c11f..b2961c4 100644
--- a/contexthub/1.1/vts/functional/Android.bp
+++ b/contexthub/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalContexthubV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/contexthub/common/vts/Android.bp b/contexthub/common/vts/Android.bp
index 3d5196a..ae56a6c 100644
--- a/contexthub/common/vts/Android.bp
+++ b/contexthub/common/vts/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test_library {
     name: "VtsHalContexthubUtils",
     srcs: [
diff --git a/current.txt b/current.txt
index 6696516..88b067d 100644
--- a/current.txt
+++ b/current.txt
@@ -765,3 +765,78 @@
 2ce1f7fb52e49f80b13a9b153d491bce530552f02357ea729acae922a8659f93 android.hardware.wifi.supplicant@1.3::ISupplicantStaIfaceCallback
 77531c8d048f8f8ae532babd0ca86332a865ec9aace1b051226ef2b21123e645 android.hardware.wifi.supplicant@1.3::ISupplicantStaNetwork
 98592d193a717066facf91428426e5abe211e3bd718bc372e29fb944ddbe6e7c android.hardware.wifi.supplicant@1.3::types
+
+# ABI preserving changes to HALs during Android S
+2c331a9605f3a08d9c1e0a36169ca57758bc43c11a78ef3f3730509885e52c15 android.hardware.graphics.composer@2.4::IComposerClient
+3da3ce039247872d95c6bd48621dbfdfa1c2d2a91a90f257862f87ee2bc46300 android.hardware.health@2.1::types
+9679f27a42f75781c8993ef163ed92808a1928de186639834841d0b8e326e63d android.hardware.gatekeeper@1.0::IGatekeeper
+9c4eb603d7b9ad675a14edb6180681c5a78da5c6bdc7755853912c974a21f7e5 android.hardware.gnss@1.0::IAGnssCallback
+40456eb90ea88b62d18ad3fbf1da8917981cd55ac04ce69c8e058d49ff5beff4 android.hardware.keymaster@3.0::IKeymasterDevice
+6017b4f2481feb0fffceae81c62bc372c898998b2d8fe69fbd39859d3a315e5e android.hardware.keymaster@4.0::IKeymasterDevice
+dabe23dde7c9e3ad65c61def7392f186d7efe7f4216f9b6f9cf0863745b1a9f4 android.hardware.keymaster@4.1::IKeymasterDevice
+cd84ab19c590e0e73dd2307b591a3093ee18147ef95e6d5418644463a6620076 android.hardware.neuralnetworks@1.2::IDevice
+f729ee6a5f136b25d79ea6895d24700fce413df555baaecf2c39e4440d15d043 android.hardware.neuralnetworks@1.0::types
+38c1a3eb5c3dfa4cc40b7cf4be0e9850440e2c57197fba7407081679b358aa22 android.hardware.neuralnetworks@1.2::types
+550619f876cadbea1f718edce120f0e1dd4a6f4bd4c28b59d479677dc86b0aec android.hardware.neuralnetworks@1.3::types
+c3fec5bd470984402997f78a74b6511efc4063b270f2bd9ee7b78f48b683a1bb android.hardware.neuralnetworks@1.3::IDevice
+0fdfad62c2ec33b52e6687004e5a1971c02d10b93ee4d26df5ccff7ce032494a android.hardware.neuralnetworks@1.3::IPreparedModel
+b40c13f9a9affc806c778c1f8c78e90d4acb50f1d6a6be185d933d7a04b91c5b android.hardware.sensors@1.0::ISensors
+432086950205f5876da85dbd42004b0d0d05b429b9494b4f76a4d888758c5bd8 android.hardware.sensors@1.0::types
+e8c86c69c438da8d1549856c1bb3e2d1b8da52722f8235ff49a30f2cce91742c android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
+b9fbb6e2e061ed0960939d48b785e9700210add1f13ed32ecd688d0f1ca20ef7 android.hardware.renderscript@1.0::types
+0f53d70e1eadf8d987766db4bf6ae2048004682168f4cab118da576787def3fa android.hardware.radio@1.0::types
+38d65fb20c60a5b823298560fc0825457ecdc49603a4b4e94bf81511790737da android.hardware.radio@1.4::types
+954c334efd80e8869b66d1ce5fe2755712d96ba4b3c38d415739c330af5fb4cb android.hardware.radio@1.5::types
+cfaab0e45c5d7b3595032d649da29ed712e920f956c13671efd35602fa81c923 android.hardware.radio@1.0::IRadio
+89d78fa49b09e2f31812bb63e1bfac2bf318a9561473c6b0ed6904ce18377d54 android.hardware.radio@1.0::IRadioIndication
+bc3c8c233085fca3879dc74b490b9e5bc1063258470d3b4c12f7a74bf215cbbd android.hardware.radio@1.0::IRadioResponse
+86fb079a600b2301a752249dfbfc53983a795d752f11aabcb68315a189f6c9a2 android.hardware.radio@1.1::IRadio
+00366b2f88f9ec2458014972938270c8413d4ab303218e37bf3add2b8e6b829a android.hardware.radio@1.1::IRadioResponse
+2b5afef68e3e2ff1dab63e4f2ee57337ef2635ec812f49080cadfce966d33b52 android.hardware.radio@1.2::IRadio
+
+# HALs released in Android S
+59fa68432e374c8d3b7ec098a91a1e023a2c728110bb733237c551afa5929725 android.hardware.audio@7.0::IDevice
+2207948ca127b801c94f667c99dfd139f150b50671e1408d3e855d03efbf631d android.hardware.audio@7.0::IDevicesFactory
+1d201e15c553cd44c62864ac8d7039351ddf048a7ee61e380f6efb0904442eb8 android.hardware.audio@7.0::IPrimaryDevice
+38afa920e6d36013b5a800e8c82eefeebd24602de24441e2f8ce5b3bdf62d3af android.hardware.audio@7.0::IStream
+77d84330418abba5a92b0cdc4e27fa7c85c27344eaf7eeef441b8e88829ee475 android.hardware.audio@7.0::IStreamIn
+9e4d79ed8f3c7e18455f371342ea1802f080bae38f64db746cc433305ee1978b android.hardware.audio@7.0::IStreamOut
+54cbc3c637fe8d4b889ccb5690e5e3069ca8efd9c6607ce1d021a3f47576c67e android.hardware.audio@7.0::IStreamOutCallback
+8036ae0a68a698a79207218018de5f41aed344723f644112ffc99e20e5e2e9ff android.hardware.audio@7.0::IStreamOutEventCallback
+84978dbd15d4fa8be6073d0974755f7718ee0cde519ce71449fb734f53cee46b android.hardware.audio@7.0::types
+6a03a9d8cc917da00e8b88f4abc42db2f741e2d50901e8ab6dea32084a238fbd android.hardware.audio.common@7.0::types
+842b4485a00005fb938f674b12445cb592cd1636f56c7cc447966119070811bd android.hardware.audio.effect@7.0::IAcousticEchoCancelerEffect
+b62a85e5d745dc35b5a60464c6b33a5bb7a2b8b95863a1374aee77ea29cf8f49 android.hardware.audio.effect@7.0::IAutomaticGainControlEffect
+c8d5e30848191713db7cffccc482e4427816f33c98a24734c8769962f79f855b android.hardware.audio.effect@7.0::IBassBoostEffect
+7d021ecdf5bb6a61eb9ad193585d4986d1a64cb7fb4b52f219d7380145f2c6f1 android.hardware.audio.effect@7.0::IDownmixEffect
+7fee1e7c7bb3d513a524c8963d1f8f7c2ad856f26c745b4ebc286b40d503264a android.hardware.audio.effect@7.0::IEffect
+7596050ccc00234458dcb4e692056ed3c16f3618c11d7b17cb749cfd5713705d android.hardware.audio.effect@7.0::IEffectBufferProviderCallback
+f2e41467bcf1140a11b219c2e8f77981b955c2941befe66e1cc685b7863ae4c9 android.hardware.audio.effect@7.0::IEffectsFactory
+af66fb4addbc477f9fea65fb63475203122a9189624ca8d14e757bc7826d60a4 android.hardware.audio.effect@7.0::IEnvironmentalReverbEffect
+2878d007ed55e1a4149ddcd29606962c948d8610642276f91dffd5ed32281824 android.hardware.audio.effect@7.0::IEqualizerEffect
+0260ef9e2a3e077de366ebebc0c117c7ee13f46a1eabd4abd66cc6245d0bed98 android.hardware.audio.effect@7.0::ILoudnessEnhancerEffect
+3586bbc3a7cbe30f9aff0a522524eea9b78eea78280f09c35d43dbab48a1193e android.hardware.audio.effect@7.0::INoiseSuppressionEffect
+a7d74d7e7e0b1e3b739f233b7776bf01e868856a536f5cdac0f307e9c2850e64 android.hardware.audio.effect@7.0::IPresetReverbEffect
+b4cbc1f2d38787f2ad069a8e4d10c0896287531a2596f0de0283e390b0ecf05d android.hardware.audio.effect@7.0::IVirtualizerEffect
+2b5681e1ea6a2db0dc1e84edb96d3de2f7daf306046543e7956be76dcb8f20fb android.hardware.audio.effect@7.0::IVisualizerEffect
+fa1e2d78e66fd662de93cb479ffd55947fe54f51cb53915814b3d3e3036c86a5 android.hardware.audio.effect@7.0::types
+4baf8e0eca4aa896cc9ceb7bb676aaf4fa21372ef8b49eed68eced1221c3dc0d android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvider
+d417a9212c8f96e3a06a2f221c8c5756c765355b2b81de2b2a65d4c9eee85401 android.hardware.bluetooth.audio@2.1::IBluetoothAudioProvidersFactory
+c17d9e27abd37ae5a8ff8da08fc5c9b13a264670feef6bbbc9d3ab1915216130 android.hardware.bluetooth.audio@2.1::types
+6763dd2273b1b47f3ac68af9b66870287eba33fb5b4d66e8fe1d30ae18ce24cb android.hardware.boot@1.2::IBootControl
+0c0657fad2239c2c7ec363d3b13f2e002d1c267ca89d2cc96d2b1de0475386cb android.hardware.fastboot@1.1::IFastboot
+3e8866987de4ecb48807c09d4c88ec38365930a22415f1b74edf8b14da17846b android.hardware.radio@1.6::IRadio
+715789427a44cc78f9d123b0ceb9e035e4ac2b1049501337c23a512e85b87850 android.hardware.radio@1.6::IRadioIndication
+2e9c08c4bc9539d8da28d7de33500f87148f7fa2e377238ee898b41752ac4f29 android.hardware.radio@1.6::IRadioResponse
+6475887a9cd5cc8cb803e3a78956d84d7a5fde571407ede2396f3ea5e0c0d3ad android.hardware.radio@1.6::types
+f22813615be1445ddd817655c054fc69dc9efea56c9035cd0757f3cbed190641 android.hardware.radio.config@1.3::IRadioConfig
+c9ad18729268593d14681d88ffad1c97e707444a45e1b4ed804dab949edbd84f android.hardware.radio.config@1.3::IRadioConfigResponse
+78dcb9a6975e8b377cb90bbe952078162960941468c992dcd2e1830a477b8c03 android.hardware.radio.config@1.3::types
+fd43298c43f70130c747a642ee43b0c242ac0cebffb377faa24f2725f0aa6caf android.hardware.tetheroffload.control@1.1::IOffloadControl
+ead4ec8713a2cb40906fe31ba793d21a6b1190143c446690d16a6ea686aa2fea android.hardware.tetheroffload.control@1.1::ITetheringOffloadCallback
+e34b4c7bec5e032c14804707ca924dd6b99ed5ba139da7505fe7d698d0fe178f android.hardware.tetheroffload.control@1.1::types
+
+# ABI preserving changes to HALs during Android T
+62ace52d9c3ff1f60f94118557a2aaf0b953513e59dcd34d5f94ae28d4c7e780 android.hardware.fastboot@1.0::IFastboot
+
+# There should be no more HIDL HALs - please use AIDL instead.
diff --git a/drm/1.0/Android.bp b/drm/1.0/Android.bp
index 9049af2..3aa2a4d 100644
--- a/drm/1.0/Android.bp
+++ b/drm/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.drm@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICryptoFactory.hal",
diff --git a/drm/1.0/default/Android.bp b/drm/1.0/default/Android.bp
index 1122e46..a5cba5c 100644
--- a/drm/1.0/default/Android.bp
+++ b/drm/1.0/default/Android.bp
@@ -1,3 +1,26 @@
+// Copyright (C) 2016-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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.drm@1.0-helper",
     vendor_available: true,
@@ -22,3 +45,111 @@
     ],
     export_include_dirs: ["include"],
 }
+
+soong_config_module_type {
+    name: "android_hardware_drm_1_0_multilib",
+    module_type: "cc_defaults",
+    config_namespace: "ANDROID",
+    bool_variables: ["TARGET_ENABLE_MEDIADRM_64"],
+    properties: ["compile_multilib"],
+}
+
+android_hardware_drm_1_0_multilib {
+    name: "android.hardware.drm@1.0-multilib-lib",
+    compile_multilib: "prefer32",
+    soong_config_variables: {
+        TARGET_ENABLE_MEDIADRM_64: {
+            compile_multilib: "both",
+        }
+    }
+}
+
+android_hardware_drm_1_0_multilib {
+    name: "android.hardware.drm@1.0-multilib-exe",
+    compile_multilib: "prefer32",
+    soong_config_variables: {
+        TARGET_ENABLE_MEDIADRM_64: {
+            compile_multilib: "first",
+        }
+    }
+}
+
+cc_defaults {
+    name: "android.hardware.drm@1.0-service-defaults",
+    proprietary: true,
+    relative_install_path: "hw",
+    include_dirs: ["hardware/interfaces/drm"],
+    header_libs: ["media_plugin_headers"],
+    static_libs: ["android.hardware.drm@1.0-helper"],
+
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hidl.memory@1.0",
+        "libhidlbase",
+        "libhardware",
+        "liblog",
+        "libutils",
+        "libbinder",
+    ],
+}
+
+//############ Build legacy drm service ############
+
+cc_binary {
+    name: "android.hardware.drm@1.0-service",
+    defaults: [
+        "android.hardware.drm@1.0-multilib-exe",
+        "android.hardware.drm@1.0-service-defaults"
+    ],
+    init_rc: ["android.hardware.drm@1.0-service.rc"],
+    srcs: ["service.cpp"],
+}
+
+//############ Build legacy drm lazy service ############
+
+cc_binary {
+    name: "android.hardware.drm@1.0-service-lazy",
+    defaults: [
+        "android.hardware.drm@1.0-multilib-exe",
+        "android.hardware.drm@1.0-service-defaults"
+    ],
+    overrides: ["android.hardware.drm@1.0-service"],
+    init_rc: ["android.hardware.drm@1.0-service-lazy.rc"],
+    srcs: ["serviceLazy.cpp"],
+}
+
+//############ Build legacy drm impl library ############
+
+cc_library_shared {
+    name: "android.hardware.drm@1.0-impl",
+    defaults: ["android.hardware.drm@1.0-multilib-lib"],
+    proprietary: true,
+    relative_install_path: "hw",
+
+    include_dirs: [
+        "frameworks/native/include",
+        "frameworks/av/include",
+    ],
+
+    shared_libs: [
+        "android.hardware.drm@1.0",
+        "android.hidl.memory@1.0",
+        "libcutils",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libstagefright_foundation",
+        "libutils",
+    ],
+
+    static_libs: ["android.hardware.drm@1.0-helper"],
+
+    srcs: [
+        "DrmFactory.cpp",
+        "DrmPlugin.cpp",
+        "CryptoFactory.cpp",
+        "CryptoPlugin.cpp",
+        "LegacyPluginPath.cpp",
+        "TypeConvert.cpp",
+    ],
+}
diff --git a/drm/1.0/default/Android.mk b/drm/1.0/default/Android.mk
deleted file mode 100644
index 9016dc3..0000000
--- a/drm/1.0/default/Android.mk
+++ /dev/null
@@ -1,81 +0,0 @@
-#
-# Copyright (C) 2016 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.
-
-
-############# Build legacy drm service ############
-
-LOCAL_PATH := $(call my-dir)
-
-include $(CLEAR_VARS)
-
-include $(LOCAL_PATH)/common_default_service.mk
-LOCAL_MODULE := android.hardware.drm@1.0-service
-LOCAL_INIT_RC := android.hardware.drm@1.0-service.rc
-LOCAL_SRC_FILES := service.cpp
-
-include $(BUILD_EXECUTABLE)
-
-############# Build legacy drm lazy service ############
-
-include $(CLEAR_VARS)
-
-include $(LOCAL_PATH)/common_default_service.mk
-LOCAL_MODULE := android.hardware.drm@1.0-service-lazy
-LOCAL_OVERRIDES_MODULES := android.hardware.drm@1.0-service
-LOCAL_INIT_RC := android.hardware.drm@1.0-service-lazy.rc
-LOCAL_SRC_FILES := serviceLazy.cpp
-
-include $(BUILD_EXECUTABLE)
-
-############# Build legacy drm impl library ############
-
-include $(CLEAR_VARS)
-LOCAL_MODULE := android.hardware.drm@1.0-impl
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-LOCAL_SRC_FILES := \
-    DrmFactory.cpp \
-    DrmPlugin.cpp \
-    CryptoFactory.cpp \
-    CryptoPlugin.cpp \
-    LegacyPluginPath.cpp \
-    TypeConvert.cpp \
-
-LOCAL_SHARED_LIBRARIES := \
-    android.hardware.drm@1.0 \
-    android.hidl.memory@1.0 \
-    libcutils \
-    libhidlbase \
-    libhidlmemory \
-    liblog \
-    libstagefright_foundation \
-    libutils \
-
-LOCAL_STATIC_LIBRARIES := \
-    android.hardware.drm@1.0-helper \
-
-LOCAL_C_INCLUDES := \
-    frameworks/native/include \
-    frameworks/av/include
-
-# TODO: Some legacy DRM plugins only support 32-bit. They need to be migrated to
-# 64-bit. (b/18948909) Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this impl as
-# 64-bit.
-ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
-LOCAL_32_BIT_ONLY := true
-endif
-
-include $(BUILD_SHARED_LIBRARY)
diff --git a/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc b/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc
index 4b32f7f..e5ae5cd 100644
--- a/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc
+++ b/drm/1.0/default/android.hardware.drm@1.0-service-lazy.rc
@@ -7,4 +7,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/drm/1.0/default/android.hardware.drm@1.0-service.rc b/drm/1.0/default/android.hardware.drm@1.0-service.rc
index 790eded..2aba187 100644
--- a/drm/1.0/default/android.hardware.drm@1.0-service.rc
+++ b/drm/1.0/default/android.hardware.drm@1.0-service.rc
@@ -5,4 +5,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
+    task_profiles ProcessCapacityHigh
diff --git a/drm/1.0/default/common_default_service.mk b/drm/1.0/default/common_default_service.mk
deleted file mode 100644
index 1b5a975..0000000
--- a/drm/1.0/default/common_default_service.mk
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# Copyright (C) 2019 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 $(CLEAR_VARS)
-LOCAL_PROPRIETARY_MODULE := true
-LOCAL_MODULE_RELATIVE_PATH := hw
-
-LOCAL_SHARED_LIBRARIES := \
-  android.hardware.drm@1.0 \
-  android.hidl.memory@1.0 \
-  libhidlbase \
-  libhardware \
-  liblog \
-  libutils \
-  libbinder \
-
-LOCAL_STATIC_LIBRARIES := \
-  android.hardware.drm@1.0-helper \
-
-LOCAL_C_INCLUDES := \
-  hardware/interfaces/drm
-
-LOCAL_HEADER_LIBRARIES := \
-  media_plugin_headers
-
-# TODO(b/18948909) Some legacy DRM plugins only support 32-bit. They need to be
-# migrated to 64-bit. Once all of a device's legacy DRM plugins support 64-bit,
-# that device can turn on TARGET_ENABLE_MEDIADRM_64 to build this service as
-# 64-bit.
-ifneq ($(TARGET_ENABLE_MEDIADRM_64), true)
-LOCAL_32_BIT_ONLY := true
-endif
diff --git a/drm/1.0/vts/functional/Android.bp b/drm/1.0/vts/functional/Android.bp
index 8fd258a..5ea6ad3 100644
--- a/drm/1.0/vts/functional/Android.bp
+++ b/drm/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libdrmvtshelper",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -43,12 +52,12 @@
         "android.hardware.drm@1.0",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
+        "libcrypto",
         "libhidlmemory",
         "libnativehelper",
     ],
     static_libs: [
         "android.hardware.drm@1.0-helper",
-        "libcrypto_static",
         "libdrmvtshelper",
     ],
     export_shared_lib_headers: [
@@ -76,12 +85,12 @@
         "android.hardware.drm@1.0",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
+        "libcrypto",
         "libhidlmemory",
         "libnativehelper",
     ],
     static_libs: [
         "android.hardware.drm@1.0-helper",
-        "libcrypto_static",
         "libdrmvtshelper",
     ],
     arch: {
diff --git a/drm/1.0/vts/functional/drm_hal_test_main.cpp b/drm/1.0/vts/functional/drm_hal_test_main.cpp
index fd2538b..ccbf51e 100644
--- a/drm/1.0/vts/functional/drm_hal_test_main.cpp
+++ b/drm/1.0/vts/functional/drm_hal_test_main.cpp
@@ -51,21 +51,27 @@
     return allInstanceUuidCombos;
 }();
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorFactoryTest);
 INSTANTIATE_TEST_CASE_P(DrmHalVendorFactoryTestCases, DrmHalVendorFactoryTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorPluginTest);
 INSTANTIATE_TEST_CASE_P(DrmHalVendorPluginTestCases, DrmHalVendorPluginTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorDecryptTest);
 INSTANTIATE_TEST_CASE_P(DrmHalVendorDecryptTestCases, DrmHalVendorDecryptTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalClearkeyFactoryTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyFactoryTest, testing::ValuesIn(kAllInstances),
                          drm_vts::PrintParamInstanceToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalClearkeyPluginTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyPluginTest, testing::ValuesIn(kAllInstances),
                          drm_vts::PrintParamInstanceToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalClearkeyDecryptTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyDecryptTest, testing::ValuesIn(kAllInstances),
                          drm_vts::PrintParamInstanceToString);
diff --git a/drm/1.1/Android.bp b/drm/1.1/Android.bp
index 16010a6..3290203 100644
--- a/drm/1.1/Android.bp
+++ b/drm/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.drm@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICryptoFactory.hal",
diff --git a/drm/1.1/vts/functional/Android.bp b/drm/1.1/vts/functional/Android.bp
index 053a564..656ec50 100644
--- a/drm/1.1/vts/functional/Android.bp
+++ b/drm/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.drm@1.1-vts",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/drm/1.1/vts/functional/drm_hal_test_main.cpp b/drm/1.1/vts/functional/drm_hal_test_main.cpp
index c6965bd..2ecc6b4 100644
--- a/drm/1.1/vts/functional/drm_hal_test_main.cpp
+++ b/drm/1.1/vts/functional/drm_hal_test_main.cpp
@@ -48,5 +48,6 @@
     return allInstancesWithClearKeyUuid;
 }();
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalClearkeyTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTest, testing::ValuesIn(kAllInstances),
                          PrintParamInstanceToString);
diff --git a/drm/1.2/Android.bp b/drm/1.2/Android.bp
index 9104aa9..453ae0d 100644
--- a/drm/1.2/Android.bp
+++ b/drm/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.drm@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ICryptoFactory.hal",
diff --git a/drm/1.2/vts/functional/Android.bp b/drm/1.2/vts/functional/Android.bp
index 9aed4ee..ca90ee9 100644
--- a/drm/1.2/vts/functional/Android.bp
+++ b/drm/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.drm@1.2-vts",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -31,12 +40,12 @@
         "android.hardware.drm@1.2",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
+        "libcrypto",
         "libhidlmemory",
         "libnativehelper",
     ],
     static_libs: [
         "android.hardware.drm@1.0-helper",
-        "libcrypto_static",
         "libdrmvtshelper",
     ],
     export_shared_lib_headers: [
@@ -63,11 +72,11 @@
         "android.hardware.drm@1.0",
         "android.hardware.drm@1.2",
         "android.hidl.allocator@1.0",
+        "libcrypto",
         "libhidlmemory",
     ],
     static_libs: [
         "android.hardware.drm@1.0-helper",
-        "libcrypto_static",
         "libdrmvtshelper",
     ],
     arch: {
diff --git a/drm/1.2/vts/functional/drm_hal_test_main.cpp b/drm/1.2/vts/functional/drm_hal_test_main.cpp
index ea6e63d..2eb18f1 100644
--- a/drm/1.2/vts/functional/drm_hal_test_main.cpp
+++ b/drm/1.2/vts/functional/drm_hal_test_main.cpp
@@ -57,8 +57,10 @@
     return allInstanceUuidCombos;
 }();
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalTest, testing::ValuesIn(kAllInstances),
                          PrintParamInstanceToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalClearkeyTestV1_2);
 INSTANTIATE_TEST_SUITE_P(PerInstance, DrmHalClearkeyTestV1_2, testing::ValuesIn(kAllInstances),
                          PrintParamInstanceToString);
 
diff --git a/drm/1.3/Android.bp b/drm/1.3/Android.bp
index b0ffcb9..08f7aa8 100644
--- a/drm/1.3/Android.bp
+++ b/drm/1.3/Android.bp
@@ -1,14 +1,20 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.drm@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
-        "IDrmFactory.hal",
         "ICryptoFactory.hal",
+        "IDrmFactory.hal",
     ],
     interfaces: [
         "android.hardware.drm@1.0",
diff --git a/drm/1.3/vts/functional/Android.bp b/drm/1.3/vts/functional/Android.bp
index 3e40adf..cd1cc0f 100644
--- a/drm/1.3/vts/functional/Android.bp
+++ b/drm/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.drm@1.3-vts",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/drm/1.3/vts/functional/drm_hal_test_main.cpp b/drm/1.3/vts/functional/drm_hal_test_main.cpp
index 02b45ea..b8f64c0 100644
--- a/drm/1.3/vts/functional/drm_hal_test_main.cpp
+++ b/drm/1.3/vts/functional/drm_hal_test_main.cpp
@@ -81,12 +81,15 @@
     return allInstanceUuidCombos;
 }();
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorFactoryTest);
 INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorFactoryTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorPluginTest);
 INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorPluginTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DrmHalVendorDecryptTest);
 INSTANTIATE_TEST_CASE_P(PerInstanceUuidV1_0, DrmHalVendorDecryptTest,
                         testing::ValuesIn(kAllInstances),
                         drm_vts::PrintParamInstanceToString);
diff --git a/dumpstate/1.0/Android.bp b/dumpstate/1.0/Android.bp
index 3d47550..4e37ce9 100644
--- a/dumpstate/1.0/Android.bp
+++ b/dumpstate/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.dumpstate@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IDumpstateDevice.hal",
     ],
diff --git a/dumpstate/1.0/default/Android.bp b/dumpstate/1.0/default/Android.bp
index 6b02715..18777a7 100644
--- a/dumpstate/1.0/default/Android.bp
+++ b/dumpstate/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.dumpstate@1.0-service.example",
     init_rc: ["android.hardware.dumpstate@1.0-service.rc"],
diff --git a/dumpstate/1.0/vts/functional/Android.bp b/dumpstate/1.0/vts/functional/Android.bp
index 451b053..cc0a9cd 100644
--- a/dumpstate/1.0/vts/functional/Android.bp
+++ b/dumpstate/1.0/vts/functional/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalDumpstateV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
index 343d4c9..0c747b2 100644
--- a/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
+++ b/dumpstate/1.0/vts/functional/VtsHalDumpstateV1_0TargetTest.cpp
@@ -100,6 +100,7 @@
     native_handle_delete(handle);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DumpstateHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, DumpstateHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
diff --git a/dumpstate/1.1/Android.bp b/dumpstate/1.1/Android.bp
index 2aa8c82..b87ca91 100644
--- a/dumpstate/1.1/Android.bp
+++ b/dumpstate/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.dumpstate@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDumpstateDevice.hal",
diff --git a/dumpstate/1.1/default/Android.bp b/dumpstate/1.1/default/Android.bp
index bd92075..29c245e 100644
--- a/dumpstate/1.1/default/Android.bp
+++ b/dumpstate/1.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.dumpstate@1.1-service.example",
     vendor: true,
diff --git a/dumpstate/1.1/default/main.cpp b/dumpstate/1.1/default/main.cpp
index 3c17e18..4510f63 100644
--- a/dumpstate/1.1/default/main.cpp
+++ b/dumpstate/1.1/default/main.cpp
@@ -116,7 +116,7 @@
 
     configureRpcThreadpool(1, true);
 
-    sp<DumpstateDevice> dumpstate(new DumpstateDevice);
+    auto dumpstate = sp<DumpstateDevice>::make();
     auto serviceRegistrar = LazyServiceRegistrar::getInstance();
 
     if (serviceRegistrar.registerService(dumpstate) != ::android::OK) {
diff --git a/dumpstate/1.1/vts/functional/Android.bp b/dumpstate/1.1/vts/functional/Android.bp
index 43a3c21..17b412e 100644
--- a/dumpstate/1.1/vts/functional/Android.bp
+++ b/dumpstate/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalDumpstateV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
index cbdd87b..1bef663 100644
--- a/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
+++ b/dumpstate/1.1/vts/functional/VtsHalDumpstateV1_1TargetTest.cpp
@@ -20,6 +20,7 @@
 #include <unistd.h>
 
 #include <functional>
+#include <tuple>
 #include <vector>
 
 #include <android/hardware/dumpstate/1.1/IDumpstateDevice.h>
@@ -27,6 +28,7 @@
 #include <cutils/native_handle.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
+#include <hidl/HidlSupport.h>
 #include <hidl/ServiceManagement.h>
 #include <log/log.h>
 
@@ -39,13 +41,18 @@
 using ::android::hardware::dumpstate::V1_1::IDumpstateDevice;
 using ::android::hardware::dumpstate::V1_1::toString;
 
-class DumpstateHidl1_1Test : public ::testing::TestWithParam<std::string> {
+// Base class common to all dumpstate HAL v1.1 tests.
+template <typename T>
+class DumpstateHidl1_1TestBase : public ::testing::TestWithParam<T> {
   protected:
     virtual void SetUp() override { GetService(); }
 
+    virtual std::string GetInstanceName() = 0;
+
     void GetService() {
-        dumpstate = IDumpstateDevice::getService(GetParam());
-        ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance";
+        const std::string instance_name = GetInstanceName();
+        dumpstate = IDumpstateDevice::getService(instance_name);
+        ASSERT_NE(dumpstate, nullptr) << "Could not get HIDL instance " << instance_name;
     }
 
     void ToggleVerboseLogging(bool enable) {
@@ -78,77 +85,76 @@
     sp<IDumpstateDevice> dumpstate;
 };
 
-#define TEST_FOR_DUMPSTATE_MODE(name, body, mode) \
-    TEST_P(DumpstateHidl1_1Test, name##_##mode) { body(DumpstateMode::mode); }
+// Tests that don't need to iterate every single DumpstateMode value for dumpstateBoard_1_1.
+class DumpstateHidl1_1GeneralTest : public DumpstateHidl1_1TestBase<std::string> {
+  protected:
+    virtual std::string GetInstanceName() override { return GetParam(); }
+};
 
-// We use a macro to define individual test cases instead of hidl_enum_range<> because some HAL
-// implementations are lazy and may call exit() at the end of dumpstateBoard(), which would cause
-// DEAD_OBJECT errors after the first iteration. Separate cases re-get the service each time as part
-// of SetUp(), and also provide better separation of concerns when specific modes are problematic.
-#define TEST_FOR_ALL_DUMPSTATE_MODES(name, body)       \
-    TEST_FOR_DUMPSTATE_MODE(name, body, FULL);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, INTERACTIVE);  \
-    TEST_FOR_DUMPSTATE_MODE(name, body, REMOTE);       \
-    TEST_FOR_DUMPSTATE_MODE(name, body, WEAR);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, CONNECTIVITY); \
-    TEST_FOR_DUMPSTATE_MODE(name, body, WIFI);         \
-    TEST_FOR_DUMPSTATE_MODE(name, body, DEFAULT);      \
-    TEST_FOR_DUMPSTATE_MODE(name, body, PROTO);
+// Tests that iterate every single DumpstateMode value for dumpstateBoard_1_1.
+class DumpstateHidl1_1PerModeTest
+    : public DumpstateHidl1_1TestBase<std::tuple<std::string, DumpstateMode>> {
+  protected:
+    virtual std::string GetInstanceName() override { return std::get<0>(GetParam()); }
+
+    DumpstateMode GetMode() { return std::get<1>(GetParam()); }
+
+    // Will only execute additional_assertions when status == expected.
+    void AssertStatusForMode(const Return<DumpstateStatus>& status, const DumpstateStatus expected,
+                             std::function<void()> additional_assertions = nullptr) {
+        ASSERT_TRUE(status.isOk())
+                << "Status should be ok and return a more specific DumpstateStatus: "
+                << status.description();
+        if (GetMode() == DumpstateMode::DEFAULT) {
+            ASSERT_EQ(expected, status)
+                    << "Required mode (DumpstateMode::" << toString(GetMode())
+                    << "): status should be DumpstateStatus::" << toString(expected)
+                    << ", but got DumpstateStatus::" << toString(status);
+        } else {
+            // The rest of the modes are optional to support, but they MUST return either the
+            // expected value or UNSUPPORTED_MODE.
+            ASSERT_TRUE(status == expected || status == DumpstateStatus::UNSUPPORTED_MODE)
+                    << "Optional mode (DumpstateMode::" << toString(GetMode())
+                    << "): status should be DumpstateStatus::" << toString(expected)
+                    << " or DumpstateStatus::UNSUPPORTED_MODE, but got DumpstateStatus::"
+                    << toString(status);
+        }
+        if (status == expected && additional_assertions != nullptr) {
+            additional_assertions();
+        }
+    }
+};
 
 constexpr uint64_t kDefaultTimeoutMillis = 30 * 1000;  // 30 seconds
 
-// Will only execute additional_assertions when status == expected.
-void AssertStatusForMode(const DumpstateMode mode, const Return<DumpstateStatus>& status,
-                         const DumpstateStatus expected,
-                         std::function<void()> additional_assertions = nullptr) {
-    ASSERT_TRUE(status.isOk()) << "Status should be ok and return a more specific DumpstateStatus: "
-                               << status.description();
-    if (mode == DumpstateMode::DEFAULT) {
-        ASSERT_EQ(expected, status) << "Required mode (DumpstateMode::" << toString(mode)
-                                    << "): status should be DumpstateStatus::" << toString(expected)
-                                    << ", but got DumpstateStatus::" << toString(status);
-    } else {
-        // The rest of the modes are optional to support, but they MUST return either the expected
-        // value or UNSUPPORTED_MODE.
-        ASSERT_TRUE(status == expected || status == DumpstateStatus::UNSUPPORTED_MODE)
-                << "Optional mode (DumpstateMode::" << toString(mode)
-                << "): status should be DumpstateStatus::" << toString(expected)
-                << " or DumpstateStatus::UNSUPPORTED_MODE, but got DumpstateStatus::"
-                << toString(status);
-    }
-    if (status == expected && additional_assertions != nullptr) {
-        additional_assertions();
-    }
-}
-
 // Negative test: make sure dumpstateBoard() doesn't crash when passed a null pointer.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestNullHandle, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestNullHandle) {
     EnableVerboseLogging();
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(nullptr, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(nullptr, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
-});
+    AssertStatusForMode(status, DumpstateStatus::ILLEGAL_ARGUMENT);
+}
 
 // Negative test: make sure dumpstateBoard() ignores a handle with no FD.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithNoFd, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestHandleWithNoFd) {
     EnableVerboseLogging();
 
     native_handle_t* handle = native_handle_create(0, 0);
     ASSERT_NE(handle, nullptr) << "Could not create native_handle";
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::ILLEGAL_ARGUMENT);
+    AssertStatusForMode(status, DumpstateStatus::ILLEGAL_ARGUMENT);
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Positive test: make sure dumpstateBoard() writes something to the FD.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestOk, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestOk) {
     EnableVerboseLogging();
 
     // Index 0 corresponds to the read end of the pipe; 1 to the write end.
@@ -160,9 +166,9 @@
     handle->data[0] = fds[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds]() {
+    AssertStatusForMode(status, DumpstateStatus::OK, [&fds]() {
         // Check that at least one byte was written.
         char buff;
         ASSERT_EQ(1, read(fds[0], &buff, 1)) << "Dumped nothing";
@@ -170,10 +176,10 @@
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Positive test: make sure dumpstateBoard() doesn't crash with two FDs.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestHandleWithTwoFds, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestHandleWithTwoFds) {
     EnableVerboseLogging();
 
     int fds1[2];
@@ -187,9 +193,9 @@
     handle->data[1] = fds2[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
-    AssertStatusForMode(mode, status, DumpstateStatus::OK, [&fds1, &fds2]() {
+    AssertStatusForMode(status, DumpstateStatus::OK, [&fds1, &fds2]() {
         // Check that at least one byte was written to one of the FDs.
         char buff;
         size_t read1 = read(fds1[0], &buff, 1);
@@ -200,10 +206,10 @@
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Make sure dumpstateBoard_1_1 actually validates its arguments.
-TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Negative) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestInvalidModeArgument_Negative) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -225,7 +231,7 @@
     native_handle_delete(handle);
 }
 
-TEST_P(DumpstateHidl1_1Test, TestInvalidModeArgument_Undefined) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestInvalidModeArgument_Undefined) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -248,7 +254,7 @@
 }
 
 // Positive test: make sure dumpstateBoard() from 1.0 doesn't fail.
-TEST_P(DumpstateHidl1_1Test, Test1_0MethodOk) {
+TEST_P(DumpstateHidl1_1GeneralTest, Test1_0MethodOk) {
     EnableVerboseLogging();
 
     int fds[2];
@@ -272,7 +278,7 @@
 
 // Make sure disabling verbose logging behaves correctly. Some info is still allowed to be emitted,
 // but it can't have privacy/storage/battery impacts.
-TEST_FOR_ALL_DUMPSTATE_MODES(TestVerboseLoggingDisabled, [this](DumpstateMode mode) {
+TEST_P(DumpstateHidl1_1PerModeTest, TestDeviceLoggingDisabled) {
     DisableVerboseLogging();
 
     // Index 0 corresponds to the read end of the pipe; 1 to the write end.
@@ -284,31 +290,31 @@
     handle->data[0] = fds[1];
 
     Return<DumpstateStatus> status =
-            dumpstate->dumpstateBoard_1_1(handle, mode, kDefaultTimeoutMillis);
+            dumpstate->dumpstateBoard_1_1(handle, GetMode(), kDefaultTimeoutMillis);
 
     // We don't include additional assertions here about the file passed in. If verbose logging is
     // disabled, the OEM may choose to include nothing at all, but it is allowed to include some
     // essential information based on the mode as long as it isn't private user information.
-    AssertStatusForMode(mode, status, DumpstateStatus::OK);
+    AssertStatusForMode(status, DumpstateStatus::OK);
 
     native_handle_close(handle);
     native_handle_delete(handle);
-});
+}
 
 // Double-enable is perfectly valid, but the second call shouldn't do anything.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedEnable) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedEnable) {
     EnableVerboseLogging();
     EnableVerboseLogging();
 }
 
 // Double-disable is perfectly valid, but the second call shouldn't do anything.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedDisable) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedDisable) {
     DisableVerboseLogging();
     DisableVerboseLogging();
 }
 
 // Toggling in short order is perfectly valid.
-TEST_P(DumpstateHidl1_1Test, TestRepeatedToggle) {
+TEST_P(DumpstateHidl1_1GeneralTest, TestRepeatedToggle) {
     EnableVerboseLogging();
     DisableVerboseLogging();
     EnableVerboseLogging();
@@ -316,8 +322,23 @@
 }
 
 INSTANTIATE_TEST_SUITE_P(
-        PerInstance, DumpstateHidl1_1Test,
+        PerInstance, DumpstateHidl1_1GeneralTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDumpstateDevice::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
+// Includes the mode's name as part of the description string.
+static inline std::string PrintInstanceNameToStringWithMode(
+        const testing::TestParamInfo<std::tuple<std::string, DumpstateMode>>& info) {
+    return android::hardware::PrintInstanceNameToString(
+                   testing::TestParamInfo(std::get<0>(info.param), info.index)) +
+           "_" + toString(std::get<1>(info.param));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+        PerInstanceAndMode, DumpstateHidl1_1PerModeTest,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IDumpstateDevice::descriptor)),
+                         testing::ValuesIn(android::hardware::hidl_enum_range<DumpstateMode>())),
+        PrintInstanceNameToStringWithMode);
+
 }  // namespace
diff --git a/fastboot/1.0/Android.bp b/fastboot/1.0/Android.bp
index ec447b8..e097d4b 100644
--- a/fastboot/1.0/Android.bp
+++ b/fastboot/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.fastboot@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IFastboot.hal",
diff --git a/fastboot/1.0/IFastboot.hal b/fastboot/1.0/IFastboot.hal
index dce3ad7..b39061c 100644
--- a/fastboot/1.0/IFastboot.hal
+++ b/fastboot/1.0/IFastboot.hal
@@ -33,7 +33,7 @@
     /**
      * Executes a fastboot OEM command.
      *
-     * @param oemCmdArgs The oem command that is passed to the fastboot HAL.
+     * @param oemCmd The oem command that is passed to the fastboot HAL.
      * @return result Returns the status SUCCESS if the operation is successful,
      *     INVALID_ARGUMENT for bad arguments,
      *     FAILURE_UNKNOWN for an invalid/unsupported command.
diff --git a/fastboot/1.0/default/Android.bp b/fastboot/1.0/default/Android.bp
index f7b3635..bb32c0d 100644
--- a/fastboot/1.0/default/Android.bp
+++ b/fastboot/1.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.fastboot@1.0-impl-mock",
     recovery: true,
diff --git a/fastboot/1.1/Android.bp b/fastboot/1.1/Android.bp
new file mode 100644
index 0000000..f08e10b
--- /dev/null
+++ b/fastboot/1.1/Android.bp
@@ -0,0 +1,23 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.fastboot@1.1",
+    root: "android.hardware",
+    srcs: [
+        "IFastboot.hal",
+    ],
+    interfaces: [
+        "android.hardware.fastboot@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/fastboot/1.1/IFastboot.hal b/fastboot/1.1/IFastboot.hal
new file mode 100644
index 0000000..c8f1ad0
--- /dev/null
+++ b/fastboot/1.1/IFastboot.hal
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+package android.hardware.fastboot@1.1;
+import android.hardware.fastboot@1.0;
+
+/**
+ * IFastboot interface implements vendor specific fastboot commands.
+ */
+interface IFastboot extends @1.0::IFastboot {
+    /**
+     * Executes an OEM specific erase after fastboot erase userdata.
+     *
+     * @return result Returns the status SUCCESS if the operation is successful,
+     *     NOT_SUPPORTED for unsupported command.
+     *     INVALID_ARGUMENT for bad arguments,
+     *     FAILURE_UNKNOWN for unknown error in the oem specific command.
+     */
+    doOemSpecificErase() generates (Result result);
+};
diff --git a/fastboot/1.1/default/Android.bp b/fastboot/1.1/default/Android.bp
new file mode 100644
index 0000000..f779753
--- /dev/null
+++ b/fastboot/1.1/default/Android.bp
@@ -0,0 +1,40 @@
+//
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
+    name: "android.hardware.fastboot@1.1-impl-mock",
+    recovery: true,
+    srcs: [
+        "Fastboot.cpp",
+    ],
+    relative_install_path: "hw",
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "libutils",
+        "libcutils",
+        "android.hardware.fastboot@1.0",
+        "android.hardware.fastboot@1.1",
+    ],
+}
diff --git a/fastboot/1.1/default/Fastboot.cpp b/fastboot/1.1/default/Fastboot.cpp
new file mode 100644
index 0000000..0b502e0
--- /dev/null
+++ b/fastboot/1.1/default/Fastboot.cpp
@@ -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.
+ */
+
+#include "Fastboot.h"
+
+namespace android {
+namespace hardware {
+namespace fastboot {
+namespace V1_1 {
+namespace implementation {
+
+// Methods from ::android::hardware::fastboot::V1_1::IFastboot follow.
+Return<void> Fastboot::getPartitionType(const hidl_string& /* partitionName */,
+                                        getPartitionType_cb _hidl_cb) {
+    _hidl_cb(FileSystemType::RAW, {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::doOemCommand(const hidl_string& /* oemCmd */, doOemCommand_cb _hidl_cb) {
+    _hidl_cb({Status::FAILURE_UNKNOWN, "Command not supported in default implementation"});
+    return Void();
+}
+
+Return<void> Fastboot::getVariant(getVariant_cb _hidl_cb) {
+    _hidl_cb("NA", {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) {
+    _hidl_cb(false, {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::getBatteryVoltageFlashingThreshold(
+        getBatteryVoltageFlashingThreshold_cb _hidl_cb) {
+    _hidl_cb(0, {Status::SUCCESS, ""});
+    return Void();
+}
+
+Return<void> Fastboot::doOemSpecificErase(doOemSpecificErase_cb _hidl_cb) {
+    _hidl_cb({Status::NOT_SUPPORTED, "Command not supported in default implementation"});
+    return Void();
+}
+
+extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* /* name */) {
+    return new Fastboot();
+}
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
diff --git a/fastboot/1.1/default/Fastboot.h b/fastboot/1.1/default/Fastboot.h
new file mode 100644
index 0000000..09b39c2
--- /dev/null
+++ b/fastboot/1.1/default/Fastboot.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+#pragma once
+
+#include <android/hardware/fastboot/1.1/IFastboot.h>
+#include <hidl/Status.h>
+
+namespace android {
+namespace hardware {
+namespace fastboot {
+namespace V1_1 {
+namespace implementation {
+
+using ::android::hardware::hidl_string;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::fastboot::V1_0::FileSystemType;
+using ::android::hardware::fastboot::V1_0::Status;
+using ::android::hardware::fastboot::V1_0::Result;
+
+struct Fastboot : public IFastboot {
+    // Methods from ::android::hardware::fastboot::V1_0::IFastboot follow.
+    Return<void> getPartitionType(const hidl_string& partitionName,
+                                  getPartitionType_cb _hidl_cb) override;
+    Return<void> doOemCommand(const hidl_string& oemCmd, doOemCommand_cb _hidl_cb) override;
+    Return<void> getVariant(getVariant_cb _hidl_cb) override;
+    Return<void> getOffModeChargeState(getOffModeChargeState_cb _hidl_cb) override;
+    Return<void> getBatteryVoltageFlashingThreshold(
+            getBatteryVoltageFlashingThreshold_cb _hidl_cb) override;
+    Return<void> doOemSpecificErase(doOemSpecificErase_cb _hidl_cb) override;
+};
+
+extern "C" IFastboot* HIDL_FETCH_IFastboot(const char* name);
+
+}  // namespace implementation
+}  // namespace V1_1
+}  // namespace fastboot
+}  // namespace hardware
+}  // namespace android
diff --git a/gatekeeper/1.0/Android.bp b/gatekeeper/1.0/Android.bp
index 5d63eaf..6318816 100644
--- a/gatekeeper/1.0/Android.bp
+++ b/gatekeeper/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gatekeeper@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IGatekeeper.hal",
@@ -13,5 +19,5 @@
     interfaces: [
         "android.hidl.base@1.0",
     ],
-    gen_java: true,
+    gen_java: false,
 }
diff --git a/gatekeeper/1.0/IGatekeeper.hal b/gatekeeper/1.0/IGatekeeper.hal
index 59dd7d1..84e8e06 100644
--- a/gatekeeper/1.0/IGatekeeper.hal
+++ b/gatekeeper/1.0/IGatekeeper.hal
@@ -15,6 +15,7 @@
  */
 package android.hardware.gatekeeper@1.0;
 
+@SensitiveData
 interface IGatekeeper {
 
 /**
diff --git a/gatekeeper/1.0/default/Android.bp b/gatekeeper/1.0/default/Android.bp
index 2be4f4d..a7723f6 100644
--- a/gatekeeper/1.0/default/Android.bp
+++ b/gatekeeper/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.gatekeeper@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/gatekeeper/1.0/software/Android.bp b/gatekeeper/1.0/software/Android.bp
index 24c81f6..098c067 100644
--- a/gatekeeper/1.0/software/Android.bp
+++ b/gatekeeper/1.0/software/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.gatekeeper@1.0-service.software",
     defaults: ["hidl_defaults"],
diff --git a/gatekeeper/1.0/software/tests/Android.bp b/gatekeeper/1.0/software/tests/Android.bp
index 3f0300d..1162719 100644
--- a/gatekeeper/1.0/software/tests/Android.bp
+++ b/gatekeeper/1.0/software/tests/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "gatekeeper-software-device-unit-tests",
 
diff --git a/gatekeeper/1.0/vts/functional/Android.bp b/gatekeeper/1.0/vts/functional/Android.bp
index 1ca966d..64b3505 100644
--- a/gatekeeper/1.0/vts/functional/Android.bp
+++ b/gatekeeper/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGatekeeperV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
index afc737c..618624e 100644
--- a/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
+++ b/gatekeeper/1.0/vts/functional/VtsHalGatekeeperV1_0TargetTest.cpp
@@ -236,6 +236,10 @@
   generatePassword(password, 0);
   enrollNewPassword(password, enrollRsp, true);
   verifyPassword(password, enrollRsp.data, 1, verifyRsp, true);
+
+  ALOGI("Testing unenrolled password doesn't verify");
+  generatePassword(password, 1);
+  verifyPassword(password, enrollRsp.data, 1, verifyRsp, false);
   ALOGI("Testing Enroll+Verify done");
 }
 
@@ -306,6 +310,8 @@
   if (first != nullptr && second != nullptr) {
     EXPECT_NE(first->user_id, second->user_id);
   }
+  // the old enrollment should be invalid now
+  verifyPassword(password, enrollRsp.data, 0, verifyRsp, false);
   ALOGI("Testing Untrusted Reenroll done");
 }
 
@@ -433,6 +439,7 @@
   ALOGI("Testing deleteAllUsers done: rsp=%" PRIi32, delAllRsp.code);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GatekeeperHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GatekeeperHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IGatekeeper::descriptor)),
diff --git a/gnss/1.0/Android.bp b/gnss/1.0/Android.bp
index d97588c..873ac05 100644
--- a/gnss/1.0/Android.bp
+++ b/gnss/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAGnss.hal",
diff --git a/gnss/1.0/IAGnssCallback.hal b/gnss/1.0/IAGnssCallback.hal
index 81f1689..11a6a5d 100644
--- a/gnss/1.0/IAGnssCallback.hal
+++ b/gnss/1.0/IAGnssCallback.hal
@@ -42,7 +42,6 @@
     /**
      * Represents the status of AGNSS augmented to support IPv4.
      */
-    @export(name="", value_prefix="GPS_")
     struct AGnssStatusIpV4 {
         AGnssType type;
         AGnssStatusValue status;
diff --git a/gnss/1.0/default/Android.bp b/gnss/1.0/default/Android.bp
index 57d8903..b0cf2c6 100644
--- a/gnss/1.0/default/Android.bp
+++ b/gnss/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.gnss@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/gnss/1.0/vts/functional/Android.bp b/gnss/1.0/vts/functional/Android.bp
index 45755e6..f27732a 100644
--- a/gnss/1.0/vts/functional/Android.bp
+++ b/gnss/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGnssV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
index c04b4d0..237e8ec 100644
--- a/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
+++ b/gnss/1.0/vts/functional/VtsHalGnssV1_0TargetTest.cpp
@@ -39,6 +39,10 @@
 using android::hardware::gnss::V1_0::IGnssMeasurement;
 using android::sp;
 
+/*
+ * Since Utils.cpp depends on Gnss Hal 2.0, the tests for Gnss Hal 1.0 will use
+ * there own version of IsAutomotiveDevice() instead of the common version.
+ */
 static bool IsAutomotiveDevice() {
     char buffer[PROPERTY_VALUE_MAX] = {0};
     property_get("ro.hardware.type", buffer, "");
@@ -492,9 +496,9 @@
  * Verifies that modern hardware supports measurement capabilities.
  */
 TEST_P(GnssHalTest, MeasurementCapabilites) {
-  if (info_called_count_ > 0 && last_info_.yearOfHw >= 2016) {
-    EXPECT_TRUE(last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENTS);
-  }
+    if (!IsAutomotiveDevice() && info_called_count_ > 0 && last_info_.yearOfHw >= 2016) {
+        EXPECT_TRUE(last_capabilities_ & IGnssCallback::Capabilities::MEASUREMENTS);
+    }
 }
 
 /*
@@ -507,6 +511,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GnssHalTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, GnssHalTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IGnss::descriptor)),
@@ -528,4 +533,4 @@
   }
 
   return RUN_ALL_TESTS();
-}
\ No newline at end of file
+}
diff --git a/gnss/1.1/Android.bp b/gnss/1.1/Android.bp
index 5294a6b..0664f48 100644
--- a/gnss/1.1/Android.bp
+++ b/gnss/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IGnss.hal",
         "IGnssCallback.hal",
diff --git a/gnss/1.1/default/Android.bp b/gnss/1.1/default/Android.bp
index 9c498d5..1629e41 100644
--- a/gnss/1.1/default/Android.bp
+++ b/gnss/1.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.gnss@1.1-service",
     init_rc: ["android.hardware.gnss@1.1-service.rc"],
diff --git a/gnss/1.1/vts/functional/Android.bp b/gnss/1.1/vts/functional/Android.bp
index 94bfb89..c59d5e7 100644
--- a/gnss/1.1/vts/functional/Android.bp
+++ b/gnss/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGnssV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/gnss/2.0/Android.bp b/gnss/2.0/Android.bp
index db5075f..e886963 100644
--- a/gnss/2.0/Android.bp
+++ b/gnss/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAGnss.hal",
diff --git a/gnss/2.0/default/Android.bp b/gnss/2.0/default/Android.bp
index 37de55d..73daff7 100644
--- a/gnss/2.0/default/Android.bp
+++ b/gnss/2.0/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.gnss@2.0-service",
     init_rc: ["android.hardware.gnss@2.0-service.rc"],
diff --git a/gnss/2.0/vts/functional/Android.bp b/gnss/2.0/vts/functional/Android.bp
index d67677a..3bbd572 100644
--- a/gnss/2.0/vts/functional/Android.bp
+++ b/gnss/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGnssV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
index 51dcf0d..3e0058f 100644
--- a/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.0/vts/functional/gnss_hal_test_cases.cpp
@@ -62,6 +62,15 @@
     auto gnssMeasurement_1_0 = gnss_hal_->getExtensionGnssMeasurement();
     ASSERT_TRUE(gnssMeasurement_2_0.isOk() && gnssMeasurement_1_1.isOk() &&
                 gnssMeasurement_1_0.isOk());
+
+    // CDD does not require Android Automotive OS devices to support
+    // GnssMeasurements.
+    if (Utils::isAutomotiveDevice()) {
+        ALOGI("Test GnssMeasurementExtension skipped. Android Automotive OS deice is not required "
+              "to support GNSS measurements.");
+        return;
+    }
+
     sp<IGnssMeasurement_2_0> iGnssMeas_2_0 = gnssMeasurement_2_0;
     sp<IGnssMeasurement_1_1> iGnssMeas_1_1 = gnssMeasurement_1_1;
     sp<IGnssMeasurement_1_0> iGnssMeas_1_0 = gnssMeasurement_1_0;
diff --git a/gnss/2.1/Android.bp b/gnss/2.1/Android.bp
index 2122399..6273ecb 100644
--- a/gnss/2.1/Android.bp
+++ b/gnss/2.1/Android.bp
@@ -1,24 +1,30 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IGnss.hal",
         "IGnssAntennaInfo.hal",
         "IGnssAntennaInfoCallback.hal",
         "IGnssCallback.hal",
+        "IGnssConfiguration.hal",
         "IGnssMeasurement.hal",
         "IGnssMeasurementCallback.hal",
-        "IGnssConfiguration.hal",
     ],
     interfaces: [
-        "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.measurement_corrections@1.0",
+        "android.hardware.gnss.measurement_corrections@1.1",
         "android.hardware.gnss.visibility_control@1.0",
         "android.hardware.gnss@1.0",
         "android.hardware.gnss@1.1",
@@ -26,4 +32,5 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    gen_java_constants: true,
 }
diff --git a/gnss/2.1/default/Android.bp b/gnss/2.1/default/Android.bp
index c4dc8fd..bc39384 100644
--- a/gnss/2.1/default/Android.bp
+++ b/gnss/2.1/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.gnss@2.1-service",
     init_rc: ["android.hardware.gnss@2.1-service.rc"],
diff --git a/gnss/2.1/vts/functional/Android.bp b/gnss/2.1/vts/functional/Android.bp
index b3051d4..67f1cc6 100644
--- a/gnss/2.1/vts/functional/Android.bp
+++ b/gnss/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGnssV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
index 16e634f..2902809 100644
--- a/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
+++ b/gnss/2.1/vts/functional/gnss_hal_test_cases.cpp
@@ -63,6 +63,15 @@
     auto gnssMeasurement_1_0 = gnss_hal_->getExtensionGnssMeasurement();
     ASSERT_TRUE(gnssMeasurement_2_1.isOk() && gnssMeasurement_2_0.isOk() &&
                 gnssMeasurement_1_1.isOk() && gnssMeasurement_1_0.isOk());
+
+    // CDD does not require Android Automotive OS devices to support
+    // GnssMeasurements.
+    if (Utils::isAutomotiveDevice()) {
+        ALOGI("Test GnssMeasurementExtension skipped. Android Automotive OS de  ice is not "
+              "required to support GNSS measurements.");
+        return;
+    }
+
     sp<IGnssMeasurement_2_1> iGnssMeas_2_1 = gnssMeasurement_2_1;
     sp<IGnssMeasurement_2_0> iGnssMeas_2_0 = gnssMeasurement_2_0;
     sp<IGnssMeasurement_1_1> iGnssMeas_1_1 = gnssMeasurement_1_1;
diff --git a/gnss/common/utils/default/Android.bp b/gnss/common/utils/default/Android.bp
index 577f6ae..fb83a26 100644
--- a/gnss/common/utils/default/Android.bp
+++ b/gnss/common/utils/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.gnss@common-default-lib",
     vendor_available: true,
diff --git a/gnss/common/utils/vts/Android.bp b/gnss/common/utils/vts/Android.bp
index 4c6d443..1c4cec3 100644
--- a/gnss/common/utils/vts/Android.bp
+++ b/gnss/common/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.gnss@common-vts-lib",
     vendor_available: true,
diff --git a/gnss/common/utils/vts/Utils.cpp b/gnss/common/utils/vts/Utils.cpp
index 9bf68e6..9c84e80 100644
--- a/gnss/common/utils/vts/Utils.cpp
+++ b/gnss/common/utils/vts/Utils.cpp
@@ -17,6 +17,8 @@
 #include <Utils.h>
 #include "gtest/gtest.h"
 
+#include <cutils/properties.h>
+
 namespace android {
 namespace hardware {
 namespace gnss {
@@ -194,6 +196,12 @@
     }
 }
 
+bool Utils::isAutomotiveDevice() {
+    char buffer[PROPERTY_VALUE_MAX] = {0};
+    property_get("ro.hardware.type", buffer, "");
+    return strncmp(buffer, "automotive", PROPERTY_VALUE_MAX) == 0;
+}
+
 }  // namespace common
 }  // namespace gnss
 }  // namespace hardware
diff --git a/gnss/common/utils/vts/include/Utils.h b/gnss/common/utils/vts/include/Utils.h
index 9c838b2..a4aad80 100644
--- a/gnss/common/utils/vts/include/Utils.h
+++ b/gnss/common/utils/vts/include/Utils.h
@@ -49,6 +49,8 @@
     static const MeasurementCorrections_1_1 getMockMeasurementCorrections_1_1();
 
     static GnssConstellationType_1_0 mapConstellationType(GnssConstellationType_2_0 constellation);
+
+    static bool isAutomotiveDevice();
 };
 
 }  // namespace common
diff --git a/gnss/measurement_corrections/1.0/Android.bp b/gnss/measurement_corrections/1.0/Android.bp
index 837cc7a..d75a9a7 100644
--- a/gnss/measurement_corrections/1.0/Android.bp
+++ b/gnss/measurement_corrections/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss.measurement_corrections@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IMeasurementCorrections.hal",
diff --git a/gnss/measurement_corrections/1.1/Android.bp b/gnss/measurement_corrections/1.1/Android.bp
index d279af6..1ae4ef3 100644
--- a/gnss/measurement_corrections/1.1/Android.bp
+++ b/gnss/measurement_corrections/1.1/Android.bp
@@ -1,19 +1,25 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss.measurement_corrections@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IMeasurementCorrections.hal",
     ],
     interfaces: [
         "android.hardware.gnss.measurement_corrections@1.0",
-        "android.hardware.gnss@2.0",
         "android.hardware.gnss@1.0",
+        "android.hardware.gnss@2.0",
         "android.hidl.base@1.0",
     ],
     gen_java: true,
diff --git a/gnss/visibility_control/1.0/Android.bp b/gnss/visibility_control/1.0/Android.bp
index e58e932..163595a 100644
--- a/gnss/visibility_control/1.0/Android.bp
+++ b/gnss/visibility_control/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.gnss.visibility_control@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IGnssVisibilityControl.hal",
         "IGnssVisibilityControlCallback.hal",
diff --git a/graphics/allocator/2.0/Android.bp b/graphics/allocator/2.0/Android.bp
index 37d9dfc..6ec4e64 100644
--- a/graphics/allocator/2.0/Android.bp
+++ b/graphics/allocator/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.allocator@2.0",
     root: "android.hardware",
diff --git a/graphics/allocator/2.0/default/Android.bp b/graphics/allocator/2.0/default/Android.bp
index 59229b0..4d17dc3 100644
--- a/graphics/allocator/2.0/default/Android.bp
+++ b/graphics/allocator/2.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.graphics.allocator@2.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/graphics/allocator/2.0/default/OWNERS b/graphics/allocator/2.0/default/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/allocator/2.0/default/OWNERS
+++ b/graphics/allocator/2.0/default/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/allocator/2.0/utils/OWNERS b/graphics/allocator/2.0/utils/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/allocator/2.0/utils/OWNERS
+++ b/graphics/allocator/2.0/utils/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/allocator/2.0/utils/gralloc1-adapter/Android.bp b/graphics/allocator/2.0/utils/gralloc1-adapter/Android.bp
index 9cb53e3..bc42099 100644
--- a/graphics/allocator/2.0/utils/gralloc1-adapter/Android.bp
+++ b/graphics/allocator/2.0/utils/gralloc1-adapter/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libgralloc1-adapter",
     defaults: ["hidl_defaults"],
diff --git a/graphics/allocator/2.0/utils/hal/Android.bp b/graphics/allocator/2.0/utils/hal/Android.bp
index ac642ce..6bb9a0f 100644
--- a/graphics/allocator/2.0/utils/hal/Android.bp
+++ b/graphics/allocator/2.0/utils/hal/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.allocator@2.0-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/allocator/2.0/utils/passthrough/Android.bp b/graphics/allocator/2.0/utils/passthrough/Android.bp
index 95b92cf..f5ac5a6 100644
--- a/graphics/allocator/2.0/utils/passthrough/Android.bp
+++ b/graphics/allocator/2.0/utils/passthrough/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.allocator@2.0-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/allocator/3.0/Android.bp b/graphics/allocator/3.0/Android.bp
index 2cfa1d0..768baba 100644
--- a/graphics/allocator/3.0/Android.bp
+++ b/graphics/allocator/3.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.allocator@3.0",
     root: "android.hardware",
diff --git a/graphics/allocator/4.0/Android.bp b/graphics/allocator/4.0/Android.bp
index f5f9458..0df9b39 100644
--- a/graphics/allocator/4.0/Android.bp
+++ b/graphics/allocator/4.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.allocator@4.0",
     root: "android.hardware",
diff --git a/graphics/bufferqueue/1.0/Android.bp b/graphics/bufferqueue/1.0/Android.bp
index 7fca354..eda80d7 100644
--- a/graphics/bufferqueue/1.0/Android.bp
+++ b/graphics/bufferqueue/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.bufferqueue@1.0",
     root: "android.hardware",
diff --git a/graphics/bufferqueue/2.0/Android.bp b/graphics/bufferqueue/2.0/Android.bp
index fd08079..13feeb1 100644
--- a/graphics/bufferqueue/2.0/Android.bp
+++ b/graphics/bufferqueue/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.bufferqueue@2.0",
     root: "android.hardware",
diff --git a/graphics/common/1.0/Android.bp b/graphics/common/1.0/Android.bp
index 089fe14..74a0d9b 100644
--- a/graphics/common/1.0/Android.bp
+++ b/graphics/common/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.common@1.0",
     root: "android.hardware",
diff --git a/graphics/common/1.1/Android.bp b/graphics/common/1.1/Android.bp
index 899fe03..a120278 100644
--- a/graphics/common/1.1/Android.bp
+++ b/graphics/common/1.1/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.common@1.1",
     root: "android.hardware",
diff --git a/graphics/common/1.2/Android.bp b/graphics/common/1.2/Android.bp
index 2c4d93b..fe149e4 100644
--- a/graphics/common/1.2/Android.bp
+++ b/graphics/common/1.2/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.common@1.2",
     root: "android.hardware",
diff --git a/graphics/common/aidl/Android.bp b/graphics/common/aidl/Android.bp
index e594233..cadd13c 100644
--- a/graphics/common/aidl/Android.bp
+++ b/graphics/common/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.graphics.common",
     host_supported: true,
@@ -6,6 +15,7 @@
         enabled: true,
         support_system_process: true,
     },
+    vndk_use_version: "2",
     srcs: [
         "android/hardware/graphics/common/*.aidl",
     ],
@@ -24,6 +34,7 @@
             apex_available: [
                 "//apex_available:platform",
                 "com.android.media.swcodec",
+                "com.android.neuralnetworks",
             ],
             min_sdk_version: "29",
         },
diff --git a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
index 8b12169..232e023 100644
--- a/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/aidl_api/android.hardware.graphics.common/current/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -21,7 +21,7 @@
   int width;
   int height;
   int layers;
-  android.hardware.graphics.common.PixelFormat format;
-  android.hardware.graphics.common.BufferUsage usage;
+  android.hardware.graphics.common.PixelFormat format = android.hardware.graphics.common.PixelFormat.UNSPECIFIED;
+  android.hardware.graphics.common.BufferUsage usage = android.hardware.graphics.common.BufferUsage.CPU_READ_NEVER;
   int stride;
 }
diff --git a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
index e1e3492..078c512 100644
--- a/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
+++ b/graphics/common/aidl/android/hardware/graphics/common/HardwareBufferDescription.aidl
@@ -29,7 +29,7 @@
     int width;
     int height;
     int layers;
-    PixelFormat format;
-    BufferUsage usage;
+    PixelFormat format = PixelFormat.UNSPECIFIED;
+    BufferUsage usage = BufferUsage.CPU_READ_NEVER;
     int stride;
 }
diff --git a/graphics/composer/2.1/Android.bp b/graphics/composer/2.1/Android.bp
index 4e4b81c..2e41208 100644
--- a/graphics/composer/2.1/Android.bp
+++ b/graphics/composer/2.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.composer@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IComposer.hal",
diff --git a/graphics/composer/2.1/default/Android.bp b/graphics/composer/2.1/default/Android.bp
index a367457..96fea4e 100644
--- a/graphics/composer/2.1/default/Android.bp
+++ b/graphics/composer/2.1/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.graphics.composer@2.1-service",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.1/default/OWNERS b/graphics/composer/2.1/default/OWNERS
index db8fb80..709c4d1 100644
--- a/graphics/composer/2.1/default/OWNERS
+++ b/graphics/composer/2.1/default/OWNERS
@@ -1,6 +1,3 @@
 # Graphics team
-courtneygo@google.com
-jessehall@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.1/default/android.hardware.graphics.composer@2.1-service.rc b/graphics/composer/2.1/default/android.hardware.graphics.composer@2.1-service.rc
index cbd589a..c8fccdc 100644
--- a/graphics/composer/2.1/default/android.hardware.graphics.composer@2.1-service.rc
+++ b/graphics/composer/2.1/default/android.hardware.graphics.composer@2.1-service.rc
@@ -5,4 +5,4 @@
     group graphics drmrpc
     capabilities SYS_NICE
     onrestart restart surfaceflinger
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/graphics/composer/2.1/utils/OWNERS b/graphics/composer/2.1/utils/OWNERS
index 5acc631..7af69b4 100644
--- a/graphics/composer/2.1/utils/OWNERS
+++ b/graphics/composer/2.1/utils/OWNERS
@@ -1,5 +1,2 @@
-courtneygo@google.com
-jessehall@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.1/utils/command-buffer/Android.bp b/graphics/composer/2.1/utils/command-buffer/Android.bp
index 140d9bb..07dea31 100644
--- a/graphics/composer/2.1/utils/command-buffer/Android.bp
+++ b/graphics/composer/2.1/utils/command-buffer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.1-command-buffer",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.1/utils/hal/Android.bp b/graphics/composer/2.1/utils/hal/Android.bp
index ea3666d..874be84 100644
--- a/graphics/composer/2.1/utils/hal/Android.bp
+++ b/graphics/composer/2.1/utils/hal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.1-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp b/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
index ec7a0b9..0171dd6 100644
--- a/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
+++ b/graphics/composer/2.1/utils/hwc2on1adapter/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "libhwc2on1adapter",
     vendor: true,
diff --git a/graphics/composer/2.1/utils/hwc2onfbadapter/Android.bp b/graphics/composer/2.1/utils/hwc2onfbadapter/Android.bp
index 73a41f7..3965d12 100644
--- a/graphics/composer/2.1/utils/hwc2onfbadapter/Android.bp
+++ b/graphics/composer/2.1/utils/hwc2onfbadapter/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "libhwc2onfbadapter",
     vendor: true,
diff --git a/graphics/composer/2.1/utils/passthrough/Android.bp b/graphics/composer/2.1/utils/passthrough/Android.bp
index 4d3c976..67f5163 100644
--- a/graphics/composer/2.1/utils/passthrough/Android.bp
+++ b/graphics/composer/2.1/utils/passthrough/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.1-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.1/utils/resources/Android.bp b/graphics/composer/2.1/utils/resources/Android.bp
index ed827fe..6dbd7f5 100644
--- a/graphics/composer/2.1/utils/resources/Android.bp
+++ b/graphics/composer/2.1/utils/resources/Android.bp
@@ -13,7 +13,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-cc_library_shared {
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
     name: "android.hardware.graphics.composer@2.1-resources",
     defaults: ["hidl_defaults"],
     vendor_available: true,
diff --git a/graphics/composer/2.1/utils/vts/Android.bp b/graphics/composer/2.1/utils/vts/Android.bp
index 3b0911f..8732d53 100644
--- a/graphics/composer/2.1/utils/vts/Android.bp
+++ b/graphics/composer/2.1/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.composer@2.1-vts",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.1/vts/functional/Android.bp b/graphics/composer/2.1/vts/functional/Android.bp
index e137afb..eb67d34 100644
--- a/graphics/composer/2.1/vts/functional/Android.bp
+++ b/graphics/composer/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsComposerV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,6 +30,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
diff --git a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
index b92279d..2604be6 100644
--- a/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
+++ b/graphics/composer/2.1/vts/functional/VtsHalGraphicsComposerV2_1TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <composer-vts/2.1/ComposerVts.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -1100,3 +1101,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/graphics/composer/2.2/Android.bp b/graphics/composer/2.2/Android.bp
index 930cadc..e44a236 100644
--- a/graphics/composer/2.2/Android.bp
+++ b/graphics/composer/2.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.composer@2.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IComposer.hal",
         "IComposerClient.hal",
diff --git a/graphics/composer/2.2/default/Android.mk b/graphics/composer/2.2/default/Android.mk
index 156ecb6..6f7ef85 100644
--- a/graphics/composer/2.2/default/Android.mk
+++ b/graphics/composer/2.2/default/Android.mk
@@ -2,6 +2,9 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.graphics.composer@2.2-service
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../../NOTICE
 LOCAL_VENDOR_MODULE := true
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_CFLAGS := -Wall -Werror -DLOG_TAG=\"ComposerHal\"
diff --git a/graphics/composer/2.2/default/OWNERS b/graphics/composer/2.2/default/OWNERS
index db8fb80..709c4d1 100644
--- a/graphics/composer/2.2/default/OWNERS
+++ b/graphics/composer/2.2/default/OWNERS
@@ -1,6 +1,3 @@
 # Graphics team
-courtneygo@google.com
-jessehall@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.2/default/android.hardware.graphics.composer@2.2-service.rc b/graphics/composer/2.2/default/android.hardware.graphics.composer@2.2-service.rc
index efe6dad..7714119 100644
--- a/graphics/composer/2.2/default/android.hardware.graphics.composer@2.2-service.rc
+++ b/graphics/composer/2.2/default/android.hardware.graphics.composer@2.2-service.rc
@@ -4,4 +4,4 @@
     group graphics drmrpc
     capabilities SYS_NICE
     onrestart restart surfaceflinger
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/graphics/composer/2.2/utils/OWNERS b/graphics/composer/2.2/utils/OWNERS
index 3f1e82c..709c4d1 100644
--- a/graphics/composer/2.2/utils/OWNERS
+++ b/graphics/composer/2.2/utils/OWNERS
@@ -1,5 +1,3 @@
 # Graphics team
-courtneygo@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.2/utils/command-buffer/Android.bp b/graphics/composer/2.2/utils/command-buffer/Android.bp
index c4ceaab..d55145e 100644
--- a/graphics/composer/2.2/utils/command-buffer/Android.bp
+++ b/graphics/composer/2.2/utils/command-buffer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.2-command-buffer",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.2/utils/hal/Android.bp b/graphics/composer/2.2/utils/hal/Android.bp
index f334a11..4e028e0 100644
--- a/graphics/composer/2.2/utils/hal/Android.bp
+++ b/graphics/composer/2.2/utils/hal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.2-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.2/utils/passthrough/Android.bp b/graphics/composer/2.2/utils/passthrough/Android.bp
index 318ce91..b700344 100644
--- a/graphics/composer/2.2/utils/passthrough/Android.bp
+++ b/graphics/composer/2.2/utils/passthrough/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.2-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.2/utils/resources/Android.bp b/graphics/composer/2.2/utils/resources/Android.bp
index 752eb81..9e45ef2 100644
--- a/graphics/composer/2.2/utils/resources/Android.bp
+++ b/graphics/composer/2.2/utils/resources/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.graphics.composer@2.2-resources",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.2/utils/vts/Android.bp b/graphics/composer/2.2/utils/vts/Android.bp
index a8bb1a2..5c085cb 100644
--- a/graphics/composer/2.2/utils/vts/Android.bp
+++ b/graphics/composer/2.2/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.composer@2.2-vts",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.2/vts/functional/Android.bp b/graphics/composer/2.2/vts/functional/Android.bp
index d80845f..7fe453c 100644
--- a/graphics/composer/2.2/vts/functional/Android.bp
+++ b/graphics/composer/2.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsComposerV2_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -27,6 +36,7 @@
         "libEGL",
         "libGLESv1_CM",
         "libGLESv2",
+        "libbase",
         "libfmq",
         "libgui",
         "libhidlbase",
diff --git a/graphics/composer/2.2/vts/functional/OWNERS b/graphics/composer/2.2/vts/functional/OWNERS
index a17a50c..ea06752 100644
--- a/graphics/composer/2.2/vts/functional/OWNERS
+++ b/graphics/composer/2.2/vts/functional/OWNERS
@@ -1,8 +1,6 @@
 # Graphics team
-courtneygo@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
 
 # VTS team
 yim@google.com
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
index cb43e64..b0eb4ef 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2ReadbackTest.cpp
@@ -1386,6 +1386,7 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsBlendModeCompositionTest);
 INSTANTIATE_TEST_CASE_P(
         BlendModeTest, GraphicsBlendModeCompositionTest,
         testing::Combine(
diff --git a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
index 95a0f69..f4c0382 100644
--- a/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
+++ b/graphics/composer/2.2/vts/functional/VtsHalGraphicsComposerV2_2TargetTest.cpp
@@ -17,6 +17,7 @@
 #define LOG_TAG "graphics_composer_hidl_hal_test@2.2"
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
 #include <composer-vts/2.1/TestCommandReader.h>
@@ -698,3 +699,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.3/Android.bp b/graphics/composer/2.3/Android.bp
index a777556..3c52b6f 100644
--- a/graphics/composer/2.3/Android.bp
+++ b/graphics/composer/2.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.composer@2.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IComposer.hal",
         "IComposerClient.hal",
diff --git a/graphics/composer/2.3/default/Android.bp b/graphics/composer/2.3/default/Android.bp
index a5696dd..f801fba 100644
--- a/graphics/composer/2.3/default/Android.bp
+++ b/graphics/composer/2.3/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.graphics.composer@2.3-service",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.3/default/OWNERS b/graphics/composer/2.3/default/OWNERS
index 820ebe6..709c4d1 100644
--- a/graphics/composer/2.3/default/OWNERS
+++ b/graphics/composer/2.3/default/OWNERS
@@ -1,5 +1,3 @@
 # Graphics team
-jessehall@google.com
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc b/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc
index 81ce890..d3835a4 100644
--- a/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc
+++ b/graphics/composer/2.3/default/android.hardware.graphics.composer@2.3-service.rc
@@ -4,4 +4,4 @@
     group graphics drmrpc
     capabilities SYS_NICE
     onrestart restart surfaceflinger
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/graphics/composer/2.3/utils/OWNERS b/graphics/composer/2.3/utils/OWNERS
index cc6d937..709c4d1 100644
--- a/graphics/composer/2.3/utils/OWNERS
+++ b/graphics/composer/2.3/utils/OWNERS
@@ -1,4 +1,3 @@
 # Graphics team
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.3/utils/command-buffer/Android.bp b/graphics/composer/2.3/utils/command-buffer/Android.bp
index 36ac297..ca7d136 100644
--- a/graphics/composer/2.3/utils/command-buffer/Android.bp
+++ b/graphics/composer/2.3/utils/command-buffer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.3-command-buffer",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.3/utils/hal/Android.bp b/graphics/composer/2.3/utils/hal/Android.bp
index 3ee9300..b475757 100644
--- a/graphics/composer/2.3/utils/hal/Android.bp
+++ b/graphics/composer/2.3/utils/hal/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.3-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.3/utils/passthrough/Android.bp b/graphics/composer/2.3/utils/passthrough/Android.bp
index 3ae6b16..68b706c 100644
--- a/graphics/composer/2.3/utils/passthrough/Android.bp
+++ b/graphics/composer/2.3/utils/passthrough/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.3-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.3/utils/vts/Android.bp b/graphics/composer/2.3/utils/vts/Android.bp
index 3d81e8f..7bc07a4 100644
--- a/graphics/composer/2.3/utils/vts/Android.bp
+++ b/graphics/composer/2.3/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.composer@2.3-vts",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.3/vts/functional/Android.bp b/graphics/composer/2.3/vts/functional/Android.bp
index 1ab6b3b..d27a151 100644
--- a/graphics/composer/2.3/vts/functional/Android.bp
+++ b/graphics/composer/2.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsComposerV2_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,6 +30,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libhidlbase",
         "libsync",
diff --git a/graphics/composer/2.3/vts/functional/OWNERS b/graphics/composer/2.3/vts/functional/OWNERS
index b3ea6be..ea06752 100644
--- a/graphics/composer/2.3/vts/functional/OWNERS
+++ b/graphics/composer/2.3/vts/functional/OWNERS
@@ -1,7 +1,6 @@
 # Graphics team
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
 
 # VTS team
 yim@google.com
diff --git a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
index 94766af..63af900 100644
--- a/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
+++ b/graphics/composer/2.3/vts/functional/VtsHalGraphicsComposerV2_3TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <algorithm>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.3/ComposerCommandBuffer.h>
 #include <composer-vts/2.1/GraphicsComposerCallback.h>
@@ -628,3 +629,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
\ No newline at end of file
diff --git a/graphics/composer/2.4/Android.bp b/graphics/composer/2.4/Android.bp
index 5f700be..e6b238b 100644
--- a/graphics/composer/2.4/Android.bp
+++ b/graphics/composer/2.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.composer@2.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IComposer.hal",
diff --git a/graphics/composer/2.4/IComposerClient.hal b/graphics/composer/2.4/IComposerClient.hal
index 9e3cf0e..1a59bbd 100644
--- a/graphics/composer/2.4/IComposerClient.hal
+++ b/graphics/composer/2.4/IComposerClient.hal
@@ -34,7 +34,9 @@
         /**
          * The configuration group ID (as int32_t) this config is associated to.
          * Switching between configurations within the same group may be done seamlessly
-         * in some conditions via setActiveConfigWithConstraints.
+         * in some conditions via setActiveConfigWithConstraints. Configurations which
+         * share the same config group are similar in all attributes except for the
+         * vsync period.
          */
         CONFIG_GROUP = 7,
     };
diff --git a/graphics/composer/2.4/default/Android.bp b/graphics/composer/2.4/default/Android.bp
index a30609b..7a91ec1 100644
--- a/graphics/composer/2.4/default/Android.bp
+++ b/graphics/composer/2.4/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.graphics.composer@2.4-service",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.4/default/OWNERS b/graphics/composer/2.4/default/OWNERS
index cc6d937..709c4d1 100644
--- a/graphics/composer/2.4/default/OWNERS
+++ b/graphics/composer/2.4/default/OWNERS
@@ -1,4 +1,3 @@
 # Graphics team
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc b/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc
index a296b0a..d82dcd9 100644
--- a/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc
+++ b/graphics/composer/2.4/default/android.hardware.graphics.composer@2.4-service.rc
@@ -4,4 +4,4 @@
     group graphics drmrpc
     capabilities SYS_NICE
     onrestart restart surfaceflinger
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
diff --git a/graphics/composer/2.4/utils/OWNERS b/graphics/composer/2.4/utils/OWNERS
index cc6d937..709c4d1 100644
--- a/graphics/composer/2.4/utils/OWNERS
+++ b/graphics/composer/2.4/utils/OWNERS
@@ -1,4 +1,3 @@
 # Graphics team
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
diff --git a/graphics/composer/2.4/utils/command-buffer/Android.bp b/graphics/composer/2.4/utils/command-buffer/Android.bp
index 8acf0e1..c966fc4 100644
--- a/graphics/composer/2.4/utils/command-buffer/Android.bp
+++ b/graphics/composer/2.4/utils/command-buffer/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.4-command-buffer",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.4/utils/hal/Android.bp b/graphics/composer/2.4/utils/hal/Android.bp
index f4cdea4..abf8e04 100644
--- a/graphics/composer/2.4/utils/hal/Android.bp
+++ b/graphics/composer/2.4/utils/hal/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.4-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.4/utils/passthrough/Android.bp b/graphics/composer/2.4/utils/passthrough/Android.bp
index 43d9aaa..a851c0a 100644
--- a/graphics/composer/2.4/utils/passthrough/Android.bp
+++ b/graphics/composer/2.4/utils/passthrough/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.composer@2.4-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.4/utils/vts/Android.bp b/graphics/composer/2.4/utils/vts/Android.bp
index fc13c2a..5ef861c 100644
--- a/graphics/composer/2.4/utils/vts/Android.bp
+++ b/graphics/composer/2.4/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.composer@2.4-vts",
     defaults: ["hidl_defaults"],
diff --git a/graphics/composer/2.4/vts/functional/Android.bp b/graphics/composer/2.4/vts/functional/Android.bp
index d0209b7..6089e2d 100644
--- a/graphics/composer/2.4/vts/functional/Android.bp
+++ b/graphics/composer/2.4/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsComposerV2_4TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,6 +30,7 @@
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libbase",
         "libfmq",
         "libsync",
         "android.hardware.graphics.mapper@2.0",
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/graphics/composer/2.4/vts/functional/OWNERS b/graphics/composer/2.4/vts/functional/OWNERS
index b3ea6be..ea06752 100644
--- a/graphics/composer/2.4/vts/functional/OWNERS
+++ b/graphics/composer/2.4/vts/functional/OWNERS
@@ -1,7 +1,6 @@
 # Graphics team
+adyabr@google.com
 lpy@google.com
-stoza@google.com
-vhau@google.com
 
 # VTS team
 yim@google.com
diff --git a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
index f0de4f7..46f95dd 100644
--- a/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
+++ b/graphics/composer/2.4/vts/functional/VtsHalGraphicsComposerV2_4TargetTest.cpp
@@ -19,8 +19,11 @@
 #include <algorithm>
 #include <regex>
 #include <thread>
+#include <unordered_map>
+#include <utility>
 
 #include <android-base/logging.h>
+#include <android-base/properties.h>
 #include <android/hardware/graphics/mapper/2.0/IMapper.h>
 #include <composer-command-buffer/2.4/ComposerCommandBuffer.h>
 #include <composer-vts/2.4/ComposerVts.h>
@@ -57,6 +60,30 @@
 using ContentType = IComposerClient::ContentType;
 using DisplayCapability = IComposerClient::DisplayCapability;
 
+class VtsDisplay {
+  public:
+    VtsDisplay(Display display, int32_t displayWidth, int32_t displayHeight)
+        : mDisplay(display), mDisplayWidth(displayWidth), mDisplayHeight(displayHeight) {}
+
+    Display get() const { return mDisplay; }
+
+    IComposerClient::FRect getCrop() const {
+        return {0, 0, static_cast<float>(mDisplayWidth), static_cast<float>(mDisplayHeight)};
+    }
+
+    IComposerClient::Rect getFrameRect() const { return {0, 0, mDisplayWidth, mDisplayHeight}; }
+
+    void setDimensions(int32_t displayWidth, int32_t displayHeight) {
+        mDisplayWidth = displayWidth;
+        mDisplayHeight = displayHeight;
+    }
+
+  private:
+    const Display mDisplay;
+    int32_t mDisplayWidth;
+    int32_t mDisplayHeight;
+};
+
 class GraphicsComposerHidlTest : public ::testing::TestWithParam<std::string> {
   protected:
     void SetUp() override {
@@ -67,15 +94,19 @@
         mComposerCallback = new GraphicsComposerCallback;
         mComposerClient->registerCallback_2_4(mComposerCallback);
 
-        // assume the first display is primary and is never removed
-        mPrimaryDisplay = waitForFirstDisplay();
+        // assume the first displays are built-in and are never removed
+        mDisplays = waitForDisplays();
 
         mInvalidDisplayId = GetInvalidDisplayId();
 
         // explicitly disable vsync
-        mComposerClient->setVsyncEnabled(mPrimaryDisplay, false);
+        for (const auto& display : mDisplays) {
+            mComposerClient->setVsyncEnabled(display.get(), false);
+        }
         mComposerCallback->setVsyncAllowed(false);
 
+        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
+
         mWriter = std::make_unique<CommandWriterBase>(1024);
         mReader = std::make_unique<TestCommandReader>();
     }
@@ -83,6 +114,7 @@
     void TearDown() override {
         ASSERT_EQ(0, mReader->mErrors.size());
         ASSERT_EQ(0, mReader->mCompositionChanges.size());
+
         if (mComposerCallback != nullptr) {
             EXPECT_EQ(0, mComposerCallback->getInvalidHotplugCount());
             EXPECT_EQ(0, mComposerCallback->getInvalidRefreshCount());
@@ -97,10 +129,10 @@
     // display.  Currently assuming that a device will never have close to
     // std::numeric_limit<uint64_t>::max() displays registered while running tests
     Display GetInvalidDisplayId() {
-        std::vector<Display> validDisplays = mComposerCallback->getDisplays();
         uint64_t id = std::numeric_limits<uint64_t>::max();
         while (id > 0) {
-            if (std::find(validDisplays.begin(), validDisplays.end(), id) == validDisplays.end()) {
+            if (std::none_of(mDisplays.begin(), mDisplays.end(),
+                             [&](const VtsDisplay& display) { return id == display.get(); })) {
                 return id;
             }
             id--;
@@ -127,6 +159,30 @@
 
     void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
 
+    const native_handle_t* allocate() {
+        return mGralloc->allocate(
+                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
+                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
+                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
+    }
+
+    struct TestParameters {
+        nsecs_t delayForChange;
+        bool refreshMiss;
+    };
+
+    void Test_setActiveConfigWithConstraints(const TestParameters& params);
+
+    void sendRefreshFrame(const VtsDisplay& display, const VsyncPeriodChangeTimeline*);
+
+    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
+                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
+                                  int64_t newPeriodNanos);
+
+    std::unique_ptr<ComposerClient> mComposerClient;
+    std::vector<VtsDisplay> mDisplays;
+    Display mInvalidDisplayId;
+
     void forEachTwoConfigs(Display display, std::function<void(Config, Config)> func) {
         const auto displayConfigs = mComposerClient->getDisplayConfigs(display);
         for (const Config config1 : displayConfigs) {
@@ -138,88 +194,69 @@
         }
     }
 
-    // use the slot count usually set by SF
-    static constexpr uint32_t kBufferSlotCount = 64;
-
     void Test_setContentType(const ContentType& contentType, const char* contentTypeStr);
     void Test_setContentTypeForDisplay(const Display& display,
                                        const std::vector<ContentType>& capabilities,
                                        const ContentType& contentType, const char* contentTypeStr);
 
-    std::unique_ptr<Composer> mComposer;
-    std::unique_ptr<ComposerClient> mComposerClient;
-    sp<GraphicsComposerCallback> mComposerCallback;
-    // the first display and is assumed never to be removed
-    Display mPrimaryDisplay;
-    Display mInvalidDisplayId;
-    std::unique_ptr<CommandWriterBase> mWriter;
-    std::unique_ptr<TestCommandReader> mReader;
+    Error setActiveConfigWithConstraints(
+            VtsDisplay& display, Config config,
+            const IComposerClient::VsyncPeriodChangeConstraints& constraints,
+            VsyncPeriodChangeTimeline* timeline) {
+        const auto error = mComposerClient->setActiveConfigWithConstraints(display.get(), config,
+                                                                           constraints, timeline);
+        if (error == Error::NONE) {
+            const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::WIDTH);
+            const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::HEIGHT);
+            display.setDimensions(displayWidth, displayHeight);
+        }
+        return error;
+    }
+
+    void setActiveConfig(VtsDisplay& display, Config config) {
+        mComposerClient->setActiveConfig(display.get(), config);
+        const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+                display.get(), config, IComposerClient::Attribute::WIDTH);
+        const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+                display.get(), config, IComposerClient::Attribute::HEIGHT);
+        display.setDimensions(displayWidth, displayHeight);
+    }
 
   private:
-    Display waitForFirstDisplay() {
+    // use the slot count usually set by SF
+    static constexpr uint32_t kBufferSlotCount = 64;
+
+    std::vector<VtsDisplay> waitForDisplays() {
         while (true) {
+            // Sleep for a small period of time to allow all built-in displays
+            // to post hotplug events
+            std::this_thread::sleep_for(5ms);
             std::vector<Display> displays = mComposerCallback->getDisplays();
             if (displays.empty()) {
-                usleep(5 * 1000);
                 continue;
             }
 
-            return displays[0];
+            std::vector<VtsDisplay> vtsDisplays;
+            vtsDisplays.reserve(displays.size());
+            for (Display display : displays) {
+                const Config activeConfig = mComposerClient->getActiveConfig(display);
+                const int32_t displayWidth = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::WIDTH);
+                const int32_t displayHeight = mComposerClient->getDisplayAttribute_2_4(
+                        display, activeConfig, IComposerClient::Attribute::HEIGHT);
+                vtsDisplays.emplace_back(VtsDisplay{display, displayWidth, displayHeight});
+            }
+
+            return vtsDisplays;
         }
     }
-};
 
-// Tests for IComposerClient::Command.
-class GraphicsComposerHidlCommandTest : public GraphicsComposerHidlTest {
-  protected:
-    void SetUp() override {
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::SetUp());
-
-        ASSERT_NO_FATAL_FAILURE(mGralloc = std::make_unique<Gralloc>());
-
-        const Config activeConfig = mComposerClient->getActiveConfig(mPrimaryDisplay);
-        mDisplayWidth = mComposerClient->getDisplayAttribute_2_4(mPrimaryDisplay, activeConfig,
-                                                                 IComposerClient::Attribute::WIDTH);
-        mDisplayHeight = mComposerClient->getDisplayAttribute_2_4(
-                mPrimaryDisplay, activeConfig, IComposerClient::Attribute::HEIGHT);
-
-        mWriter = std::make_unique<CommandWriterBase>(1024);
-        mReader = std::make_unique<TestCommandReader>();
-    }
-
-    void TearDown() override {
-        ASSERT_EQ(0, mReader->mErrors.size());
-        ASSERT_NO_FATAL_FAILURE(GraphicsComposerHidlTest::TearDown());
-    }
-
-    const native_handle_t* allocate() {
-        return mGralloc->allocate(
-                /*width*/ 64, /*height*/ 64, /*layerCount*/ 1,
-                static_cast<common::V1_1::PixelFormat>(PixelFormat::RGBA_8888),
-                static_cast<uint64_t>(BufferUsage::CPU_WRITE_OFTEN | BufferUsage::CPU_READ_OFTEN));
-    }
-
-    void execute() { mComposerClient->execute(mReader.get(), mWriter.get()); }
-
-    struct TestParameters {
-        nsecs_t delayForChange;
-        bool refreshMiss;
-    };
-
-    void Test_setActiveConfigWithConstraints(const TestParameters& params);
-
-    void sendRefreshFrame(const VsyncPeriodChangeTimeline*);
-
-    void waitForVsyncPeriodChange(Display display, const VsyncPeriodChangeTimeline& timeline,
-                                  int64_t desiredTimeNanos, int64_t oldPeriodNanos,
-                                  int64_t newPeriodNanos);
-
+    std::unique_ptr<Composer> mComposer;
     std::unique_ptr<CommandWriterBase> mWriter;
     std::unique_ptr<TestCommandReader> mReader;
-    int32_t mDisplayWidth;
-    int32_t mDisplayHeight;
-
-  private:
+    sp<GraphicsComposerCallback> mComposerCallback;
     std::unique_ptr<Gralloc> mGralloc;
 };
 
@@ -230,9 +267,10 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, getDisplayCapabilities) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<IComposerClient::DisplayCapability> capabilities;
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayCapabilities(display, &capabilities));
+        EXPECT_EQ(Error::NONE,
+                  mComposerClient->getDisplayCapabilities(display.get(), &capabilities));
     }
 }
 
@@ -241,38 +279,93 @@
     EXPECT_EQ(Error::BAD_DISPLAY,
               mComposerClient->getDisplayConnectionType(mInvalidDisplayId, &type));
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display, &type));
+    for (const auto& display : mDisplays) {
+        EXPECT_EQ(Error::NONE, mComposerClient->getDisplayConnectionType(display.get(), &type));
     }
 }
 
 TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4) {
-    std::vector<Config> configs = mComposerClient->getDisplayConfigs(mPrimaryDisplay);
-    for (auto config : configs) {
-        const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
-                IComposerClient::Attribute::WIDTH,
-                IComposerClient::Attribute::HEIGHT,
-                IComposerClient::Attribute::VSYNC_PERIOD,
-                IComposerClient::Attribute::CONFIG_GROUP,
-        }};
-        for (auto attribute : requiredAttributes) {
-            mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute,
-                    [&](const auto& tmpError, const auto& value) {
-                        EXPECT_EQ(Error::NONE, tmpError);
-                        EXPECT_NE(-1, value);
-                    });
-        }
+    for (const auto& display : mDisplays) {
+        std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+        for (auto config : configs) {
+            const std::array<IComposerClient::Attribute, 4> requiredAttributes = {{
+                    IComposerClient::Attribute::WIDTH,
+                    IComposerClient::Attribute::HEIGHT,
+                    IComposerClient::Attribute::VSYNC_PERIOD,
+                    IComposerClient::Attribute::CONFIG_GROUP,
+            }};
+            for (auto attribute : requiredAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute,
+                        [&](const auto& tmpError, const auto& value) {
+                            EXPECT_EQ(Error::NONE, tmpError);
+                            EXPECT_NE(-1, value);
+                        });
+            }
 
-        const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
-                IComposerClient::Attribute::DPI_X,
-                IComposerClient::Attribute::DPI_Y,
-        }};
-        for (auto attribute : optionalAttributes) {
+            const std::array<IComposerClient::Attribute, 2> optionalAttributes = {{
+                    IComposerClient::Attribute::DPI_X,
+                    IComposerClient::Attribute::DPI_Y,
+            }};
+            for (auto attribute : optionalAttributes) {
+                mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                        display.get(), config, attribute, [&](const auto& tmpError, const auto&) {
+                            EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
+                        });
+            }
+        }
+    }
+}
+
+TEST_P(GraphicsComposerHidlTest, GetDisplayAttribute_2_4_ConfigsInAGroupDifferOnlyByVsyncPeriod) {
+    struct Resolution {
+        int32_t width, height;
+    };
+    struct Dpi {
+        int32_t x, y;
+    };
+    for (const auto& display : mDisplays) {
+        std::vector<Config> configs = mComposerClient->getDisplayConfigs(display.get());
+        std::unordered_map<int32_t, Resolution> configGroupToResolutionMap;
+        std::unordered_map<int32_t, Dpi> configGroupToDpiMap;
+        for (auto config : configs) {
+            const auto configGroup = mComposerClient->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::CONFIG_GROUP);
+            const auto width = mComposerClient->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::WIDTH);
+            const auto height = mComposerClient->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::HEIGHT);
+            if (configGroupToResolutionMap.find(configGroup) == configGroupToResolutionMap.end()) {
+                configGroupToResolutionMap[configGroup] = {width, height};
+            }
+            EXPECT_EQ(configGroupToResolutionMap[configGroup].width, width);
+            EXPECT_EQ(configGroupToResolutionMap[configGroup].height, height);
+
+            int32_t dpiX = -1;
             mComposerClient->getRaw()->getDisplayAttribute_2_4(
-                    mPrimaryDisplay, config, attribute, [&](const auto& tmpError, const auto&) {
-                        EXPECT_TRUE(tmpError == Error::NONE || tmpError == Error::UNSUPPORTED);
+                    display.get(), config, IComposerClient::Attribute::DPI_X,
+                    [&](const auto& tmpError, const auto& value) {
+                        if (tmpError == Error::NONE) {
+                            dpiX = value;
+                        }
                     });
+            int32_t dpiY = -1;
+            mComposerClient->getRaw()->getDisplayAttribute_2_4(
+                    display.get(), config, IComposerClient::Attribute::DPI_Y,
+                    [&](const auto& tmpError, const auto& value) {
+                        if (tmpError == Error::NONE) {
+                            dpiY = value;
+                        }
+                    });
+            if (dpiX == -1 && dpiY == -1) {
+                continue;
+            }
+
+            if (configGroupToDpiMap.find(configGroup) == configGroupToDpiMap.end()) {
+                configGroupToDpiMap[configGroup] = {dpiX, dpiY};
+            }
+            EXPECT_EQ(configGroupToDpiMap[configGroup].x, dpiX);
+            EXPECT_EQ(configGroupToDpiMap[configGroup].y, dpiY);
         }
     }
 }
@@ -283,24 +376,25 @@
               mComposerClient->getDisplayVsyncPeriod(mInvalidDisplayId, &vsyncPeriodNanos));
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, getDisplayVsyncPeriod) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        for (Config config : mComposerClient->getDisplayConfigs(display)) {
+TEST_P(GraphicsComposerHidlTest, getDisplayVsyncPeriod) {
+    for (VtsDisplay& display : mDisplays) {
+        for (Config config : mComposerClient->getDisplayConfigs(display.get())) {
             VsyncPeriodNanos expectedVsyncPeriodNanos = mComposerClient->getDisplayAttribute_2_4(
-                    display, config, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             VsyncPeriodChangeTimeline timeline;
             IComposerClient::VsyncPeriodChangeConstraints constraints;
 
             constraints.desiredTimeNanos = systemTime();
             constraints.seamlessRequired = false;
-            EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config, constraints, &timeline));
+            EXPECT_EQ(Error::NONE,
+                      setActiveConfigWithConstraints(display, config, constraints, &timeline));
 
             if (timeline.refreshRequired) {
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, 0,
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos, 0,
                                      expectedVsyncPeriodNanos);
 
             VsyncPeriodNanos vsyncPeriodNanos;
@@ -309,7 +403,7 @@
                 std::this_thread::sleep_for(10ms);
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 --retryCount;
             } while (vsyncPeriodNanos != expectedVsyncPeriodNanos && retryCount > 0);
 
@@ -322,7 +416,7 @@
                 timeout *= 2;
                 vsyncPeriodNanos = 0;
                 EXPECT_EQ(Error::NONE,
-                          mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                          mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
                 EXPECT_EQ(vsyncPeriodNanos, expectedVsyncPeriodNanos);
             }
         }
@@ -347,32 +441,33 @@
     constraints.seamlessRequired = false;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        Config invalidConfigId = GetInvalidConfigId(display);
-        EXPECT_EQ(Error::BAD_CONFIG, mComposerClient->setActiveConfigWithConstraints(
-                                             display, invalidConfigId, constraints, &timeline));
+    for (VtsDisplay& display : mDisplays) {
+        Config invalidConfigId = GetInvalidConfigId(display.get());
+        EXPECT_EQ(Error::BAD_CONFIG,
+                  setActiveConfigWithConstraints(display, invalidConfigId, constraints, &timeline));
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_SeamlessNotAllowed) {
     VsyncPeriodChangeTimeline timeline;
     IComposerClient::VsyncPeriodChangeConstraints constraints;
 
     constraints.seamlessRequired = true;
     constraints.desiredTimeNanos = systemTime();
 
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
+    for (VtsDisplay& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
             const auto configGroup1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             const auto configGroup2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::CONFIG_GROUP);
             if (configGroup1 != configGroup2) {
-                mComposerClient->setActiveConfig(display, config1);
-                sendRefreshFrame(nullptr);
+                setActiveConfig(display, config1);
+                sendRefreshFrame(display, nullptr);
                 EXPECT_EQ(Error::SEAMLESS_NOT_ALLOWED,
-                          mComposerClient->setActiveConfigWithConstraints(display, config2,
-                                                                          constraints, &timeline));
+                          setActiveConfigWithConstraints(display, config2, constraints, &timeline));
             }
         });
     }
@@ -382,7 +477,8 @@
     return std::chrono::time_point<std::chrono::steady_clock>(std::chrono::nanoseconds(time));
 }
 
-void GraphicsComposerHidlCommandTest::sendRefreshFrame(const VsyncPeriodChangeTimeline* timeline) {
+void GraphicsComposerHidlTest::sendRefreshFrame(const VtsDisplay& display,
+                                                const VsyncPeriodChangeTimeline* timeline) {
     if (timeline != nullptr) {
         // Refresh time should be before newVsyncAppliedTimeNanos
         EXPECT_LT(timeline->refreshTimeNanos, timeline->newVsyncAppliedTimeNanos);
@@ -390,29 +486,25 @@
         std::this_thread::sleep_until(toTimePoint(timeline->refreshTimeNanos));
     }
 
-    mWriter->selectDisplay(mPrimaryDisplay);
-    mComposerClient->setPowerMode(mPrimaryDisplay, V2_1::IComposerClient::PowerMode::ON);
-    mComposerClient->setColorMode_2_3(mPrimaryDisplay, ColorMode::NATIVE,
-                                      RenderIntent::COLORIMETRIC);
+    mWriter->selectDisplay(display.get());
+    mComposerClient->setPowerMode(display.get(), V2_1::IComposerClient::PowerMode::ON);
+    mComposerClient->setColorMode_2_3(display.get(), ColorMode::NATIVE, RenderIntent::COLORIMETRIC);
 
     auto handle = allocate();
     ASSERT_NE(nullptr, handle);
 
-    IComposerClient::Rect displayFrame{0, 0, mDisplayWidth, mDisplayHeight};
-
     Layer layer;
-    ASSERT_NO_FATAL_FAILURE(
-            layer = mComposerClient->createLayer(mPrimaryDisplay, kBufferSlotCount));
+    ASSERT_NO_FATAL_FAILURE(layer = mComposerClient->createLayer(display.get(), kBufferSlotCount));
     mWriter->selectLayer(layer);
     mWriter->setLayerCompositionType(IComposerClient::Composition::DEVICE);
-    mWriter->setLayerDisplayFrame(displayFrame);
+    mWriter->setLayerDisplayFrame(display.getFrameRect());
     mWriter->setLayerPlaneAlpha(1);
-    mWriter->setLayerSourceCrop({0, 0, (float)mDisplayWidth, (float)mDisplayHeight});
+    mWriter->setLayerSourceCrop(display.getCrop());
     mWriter->setLayerTransform(static_cast<Transform>(0));
-    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerVisibleRegion(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerZOrder(10);
     mWriter->setLayerBlendMode(IComposerClient::BlendMode::NONE);
-    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, displayFrame));
+    mWriter->setLayerSurfaceDamage(std::vector<IComposerClient::Rect>(1, display.getFrameRect()));
     mWriter->setLayerBuffer(0, handle, -1);
     mWriter->setLayerDataspace(Dataspace::UNKNOWN);
 
@@ -438,11 +530,15 @@
 
     mWriter->presentDisplay();
     execute();
+
+    ASSERT_NO_FATAL_FAILURE(mComposerClient->destroyLayer(display.get(), layer));
 }
 
-void GraphicsComposerHidlCommandTest::waitForVsyncPeriodChange(
-        Display display, const VsyncPeriodChangeTimeline& timeline, int64_t desiredTimeNanos,
-        int64_t oldPeriodNanos, int64_t newPeriodNanos) {
+void GraphicsComposerHidlTest::waitForVsyncPeriodChange(Display display,
+                                                        const VsyncPeriodChangeTimeline& timeline,
+                                                        int64_t desiredTimeNanos,
+                                                        int64_t oldPeriodNanos,
+                                                        int64_t newPeriodNanos) {
     const auto CHANGE_DEADLINE = toTimePoint(timeline.newVsyncAppliedTimeNanos) + 100ms;
     while (std::chrono::steady_clock::now() <= CHANGE_DEADLINE) {
         VsyncPeriodNanos vsyncPeriodNanos;
@@ -456,17 +552,18 @@
     }
 }
 
-void GraphicsComposerHidlCommandTest::Test_setActiveConfigWithConstraints(
-        const TestParameters& params) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        forEachTwoConfigs(display, [&](Config config1, Config config2) {
-            mComposerClient->setActiveConfig(display, config1);
-            sendRefreshFrame(nullptr);
+void GraphicsComposerHidlTest::Test_setActiveConfigWithConstraints(const TestParameters& params) {
+    for (VtsDisplay& display : mDisplays) {
+        forEachTwoConfigs(display.get(), [&](Config config1, Config config2) {
+            setActiveConfig(display, config1);
+            sendRefreshFrame(display, nullptr);
 
             int32_t vsyncPeriod1 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config1, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config1,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
             int32_t vsyncPeriod2 = mComposerClient->getDisplayAttribute_2_4(
-                    display, config2, IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
+                    display.get(), config2,
+                    IComposerClient::IComposerClient::Attribute::VSYNC_PERIOD);
 
             if (vsyncPeriod1 == vsyncPeriod2) {
                 return;  // continue
@@ -476,8 +573,8 @@
             IComposerClient::VsyncPeriodChangeConstraints constraints = {
                     .desiredTimeNanos = systemTime() + params.delayForChange,
                     .seamlessRequired = false};
-            EXPECT_EQ(Error::NONE, mComposerClient->setActiveConfigWithConstraints(
-                                           display, config2, constraints, &timeline));
+            EXPECT_EQ(Error::NONE,
+                      setActiveConfigWithConstraints(display, config2, constraints, &timeline));
 
             EXPECT_TRUE(timeline.newVsyncAppliedTimeNanos >= constraints.desiredTimeNanos);
             // Refresh rate should change within a reasonable time
@@ -491,10 +588,10 @@
                     // callback
                     std::this_thread::sleep_until(toTimePoint(timeline.refreshTimeNanos) + 100ms);
                 }
-                sendRefreshFrame(&timeline);
+                sendRefreshFrame(display, &timeline);
             }
-            waitForVsyncPeriodChange(display, timeline, constraints.desiredTimeNanos, vsyncPeriod1,
-                                     vsyncPeriod2);
+            waitForVsyncPeriodChange(display.get(), timeline, constraints.desiredTimeNanos,
+                                     vsyncPeriod1, vsyncPeriod2);
 
             // At this point the refresh rate should have changed already, however in rare
             // cases the implementation might have missed the deadline. In this case a new
@@ -506,30 +603,30 @@
 
             if (newTimeline.has_value()) {
                 if (newTimeline->refreshRequired) {
-                    sendRefreshFrame(&newTimeline.value());
+                    sendRefreshFrame(display, &newTimeline.value());
                 }
-                waitForVsyncPeriodChange(display, newTimeline.value(), constraints.desiredTimeNanos,
-                                         vsyncPeriod1, vsyncPeriod2);
+                waitForVsyncPeriodChange(display.get(), newTimeline.value(),
+                                         constraints.desiredTimeNanos, vsyncPeriod1, vsyncPeriod2);
             }
 
             VsyncPeriodNanos vsyncPeriodNanos;
             EXPECT_EQ(Error::NONE,
-                      mComposerClient->getDisplayVsyncPeriod(display, &vsyncPeriodNanos));
+                      mComposerClient->getDisplayVsyncPeriod(display.get(), &vsyncPeriodNanos));
             EXPECT_EQ(vsyncPeriodNanos, vsyncPeriod2);
         });
     }
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_Delayed) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_Delayed) {
     Test_setActiveConfigWithConstraints({.delayForChange = 300'000'000,  // 300ms
                                          .refreshMiss = false});
 }
 
-TEST_P(GraphicsComposerHidlCommandTest, setActiveConfigWithConstraints_MissRefresh) {
+TEST_P(GraphicsComposerHidlTest, setActiveConfigWithConstraints_MissRefresh) {
     Test_setActiveConfigWithConstraints({.delayForChange = 0, .refreshMiss = true});
 }
 
@@ -539,9 +636,9 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setAutoLowLatencyMode) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<DisplayCapability> capabilities;
-        const auto error = mComposerClient->getDisplayCapabilities(display, &capabilities);
+        const auto error = mComposerClient->getDisplayCapabilities(display.get(), &capabilities);
         EXPECT_EQ(Error::NONE, error);
 
         const bool allmSupport =
@@ -550,16 +647,16 @@
 
         if (!allmSupport) {
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), true));
             EXPECT_EQ(Error::UNSUPPORTED,
-                      mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+                      mComposerClient->setAutoLowLatencyMode(display.get(), false));
             GTEST_SUCCEED() << "Auto Low Latency Mode is not supported on display "
-                            << std::to_string(display) << ", skipping test";
+                            << std::to_string(display.get()) << ", skipping test";
             return;
         }
 
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, true));
-        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(mPrimaryDisplay, false));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), true));
+        EXPECT_EQ(Error::NONE, mComposerClient->setAutoLowLatencyMode(display.get(), false));
     }
 }
 
@@ -572,10 +669,10 @@
 
 TEST_P(GraphicsComposerHidlTest, getSupportedContentTypes) {
     std::vector<ContentType> supportedContentTypes;
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         supportedContentTypes.clear();
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         const bool noneSupported =
                 std::find(supportedContentTypes.begin(), supportedContentTypes.end(),
                           ContentType::NONE) != supportedContentTypes.end();
@@ -585,8 +682,8 @@
 }
 
 TEST_P(GraphicsComposerHidlTest, setContentTypeNoneAlwaysAccepted) {
-    for (Display display : mComposerCallback->getDisplays()) {
-        const auto error = mComposerClient->setContentType(display, ContentType::NONE);
+    for (const auto& display : mDisplays) {
+        const auto error = mComposerClient->setContentType(display.get(), ContentType::NONE);
         EXPECT_NE(Error::UNSUPPORTED, error);
     }
 }
@@ -618,13 +715,14 @@
 
 void GraphicsComposerHidlTest::Test_setContentType(const ContentType& contentType,
                                                    const char* contentTypeStr) {
-    for (Display display : mComposerCallback->getDisplays()) {
+    for (const auto& display : mDisplays) {
         std::vector<ContentType> supportedContentTypes;
         const auto error =
-                mComposerClient->getSupportedContentTypes(display, &supportedContentTypes);
+                mComposerClient->getSupportedContentTypes(display.get(), &supportedContentTypes);
         EXPECT_EQ(Error::NONE, error);
 
-        Test_setContentTypeForDisplay(display, supportedContentTypes, contentType, contentTypeStr);
+        Test_setContentTypeForDisplay(display.get(), supportedContentTypes, contentType,
+                                      contentTypeStr);
     }
 }
 
@@ -649,12 +747,7 @@
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
         android::hardware::PrintInstanceNameToString);
 
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, GraphicsComposerHidlCommandTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IComposer::descriptor)),
-        android::hardware::PrintInstanceNameToString);
-
-TEST_P(GraphicsComposerHidlCommandTest, getLayerGenericMetadataKeys) {
+TEST_P(GraphicsComposerHidlTest, getLayerGenericMetadataKeys) {
     std::vector<IComposerClient::LayerGenericMetadataKey> keys;
     mComposerClient->getLayerGenericMetadataKeys(&keys);
 
@@ -683,3 +776,15 @@
 }  // namespace graphics
 }  // namespace hardware
 }  // namespace android
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+
+    using namespace std::chrono_literals;
+    if (!android::base::WaitForProperty("init.svc.surfaceflinger", "stopped", 10s)) {
+        ALOGE("Failed to stop init.svc.surfaceflinger");
+        return -1;
+    }
+
+    return RUN_ALL_TESTS();
+}
diff --git a/graphics/mapper/2.0/Android.bp b/graphics/mapper/2.0/Android.bp
index 4459bdc..63fdfa6 100644
--- a/graphics/mapper/2.0/Android.bp
+++ b/graphics/mapper/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.mapper@2.0",
     root: "android.hardware",
diff --git a/graphics/mapper/2.0/default/Android.bp b/graphics/mapper/2.0/default/Android.bp
index 4f64184..fffea3b 100644
--- a/graphics/mapper/2.0/default/Android.bp
+++ b/graphics/mapper/2.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.graphics.mapper@2.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.0/default/OWNERS b/graphics/mapper/2.0/default/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/2.0/default/OWNERS
+++ b/graphics/mapper/2.0/default/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/2.0/utils/OWNERS b/graphics/mapper/2.0/utils/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/2.0/utils/OWNERS
+++ b/graphics/mapper/2.0/utils/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/2.0/utils/hal/Android.bp b/graphics/mapper/2.0/utils/hal/Android.bp
index 5610a69..f5d4506 100644
--- a/graphics/mapper/2.0/utils/hal/Android.bp
+++ b/graphics/mapper/2.0/utils/hal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.mapper@2.0-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h b/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
index 8134174..0067105 100644
--- a/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
+++ b/graphics/mapper/2.0/utils/hal/include/mapper-hal/2.0/Mapper.h
@@ -85,11 +85,7 @@
             return Error::BAD_BUFFER;
         }
 
-        Error error = mHal->freeBuffer(bufferHandle);
-        if (error == Error::NONE) {
-            removeImportedBuffer(buffer);
-        }
-        return error;
+        return freeImportedBuffer(bufferHandle);
     }
 
     Return<void> lock(void* buffer, uint64_t cpuUsage, const V2_0::IMapper::Rect& accessRegion,
@@ -160,8 +156,8 @@
         return static_cast<void*>(bufferHandle);
     }
 
-    virtual native_handle_t* removeImportedBuffer(void* buffer) {
-        return static_cast<native_handle_t*>(buffer);
+    virtual Error freeImportedBuffer(native_handle_t* bufferHandle) {
+        return mHal->freeBuffer(bufferHandle);
     }
 
     virtual native_handle_t* getImportedBuffer(void* buffer) const {
diff --git a/graphics/mapper/2.0/utils/passthrough/Android.bp b/graphics/mapper/2.0/utils/passthrough/Android.bp
index 4f700c8..23450fb 100644
--- a/graphics/mapper/2.0/utils/passthrough/Android.bp
+++ b/graphics/mapper/2.0/utils/passthrough/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.mapper@2.0-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
index 85a91c3..f2e0064 100644
--- a/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
+++ b/graphics/mapper/2.0/utils/passthrough/include/mapper-passthrough/2.0/GrallocLoader.h
@@ -56,17 +56,14 @@
         return *singleton;
     }
 
+    std::mutex* getMutex() { return &mMutex; }
+
     void* add(native_handle_t* bufferHandle) {
         std::lock_guard<std::mutex> lock(mMutex);
         return mBufferHandles.insert(bufferHandle).second ? bufferHandle : nullptr;
     }
 
-    native_handle_t* remove(void* buffer) {
-        auto bufferHandle = static_cast<native_handle_t*>(buffer);
-
-        std::lock_guard<std::mutex> lock(mMutex);
-        return mBufferHandles.erase(bufferHandle) == 1 ? bufferHandle : nullptr;
-    }
+    void removeLocked(native_handle* bufferHandle) { mBufferHandles.erase(bufferHandle); }
 
     native_handle_t* get(void* buffer) {
         auto bufferHandle = static_cast<native_handle_t*>(buffer);
@@ -95,8 +92,13 @@
         return GrallocImportedBufferPool::getInstance().add(bufferHandle);
     }
 
-    native_handle_t* removeImportedBuffer(void* buffer) override {
-        return GrallocImportedBufferPool::getInstance().remove(buffer);
+    Error freeImportedBuffer(native_handle_t* bufferHandle) override {
+        std::lock_guard<std::mutex> lock(*GrallocImportedBufferPool::getInstance().getMutex());
+        Error error = this->mHal->freeBuffer(bufferHandle);
+        if (error == Error::NONE) {
+            GrallocImportedBufferPool::getInstance().removeLocked(bufferHandle);
+        }
+        return error;
     }
 
     native_handle_t* getImportedBuffer(void* buffer) const override {
diff --git a/graphics/mapper/2.0/utils/vts/Android.bp b/graphics/mapper/2.0/utils/vts/Android.bp
index e43011f..03f925d 100644
--- a/graphics/mapper/2.0/utils/vts/Android.bp
+++ b/graphics/mapper/2.0/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.mapper@2.0-vts",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/2.0/vts/OWNERS b/graphics/mapper/2.0/vts/OWNERS
index 11b7d21..4177296 100644
--- a/graphics/mapper/2.0/vts/OWNERS
+++ b/graphics/mapper/2.0/vts/OWNERS
@@ -1,7 +1,7 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
 
 # VTS team
 yim@google.com
diff --git a/graphics/mapper/2.0/vts/functional/Android.bp b/graphics/mapper/2.0/vts/functional/Android.bp
index 1f7ae04..43e6156 100644
--- a/graphics/mapper/2.0/vts/functional/Android.bp
+++ b/graphics/mapper/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsMapperV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/2.0/vts/functional/VtsHalGraphicsMapperV2_0TargetTest.cpp b/graphics/mapper/2.0/vts/functional/VtsHalGraphicsMapperV2_0TargetTest.cpp
index b079a4b..7d733ff 100644
--- a/graphics/mapper/2.0/vts/functional/VtsHalGraphicsMapperV2_0TargetTest.cpp
+++ b/graphics/mapper/2.0/vts/functional/VtsHalGraphicsMapperV2_0TargetTest.cpp
@@ -410,6 +410,7 @@
 #endif
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsMapperHidlTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, GraphicsMapperHidlTest,
         testing::Combine(
diff --git a/graphics/mapper/2.1/Android.bp b/graphics/mapper/2.1/Android.bp
index e5a5ae4..4011650 100644
--- a/graphics/mapper/2.1/Android.bp
+++ b/graphics/mapper/2.1/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.mapper@2.1",
     root: "android.hardware",
diff --git a/graphics/mapper/2.1/default/Android.bp b/graphics/mapper/2.1/default/Android.bp
index 2ea7f94..4f080c4 100644
--- a/graphics/mapper/2.1/default/Android.bp
+++ b/graphics/mapper/2.1/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.graphics.mapper@2.0-impl-2.1",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.1/default/OWNERS b/graphics/mapper/2.1/default/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/2.1/default/OWNERS
+++ b/graphics/mapper/2.1/default/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/2.1/utils/OWNERS b/graphics/mapper/2.1/utils/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/2.1/utils/OWNERS
+++ b/graphics/mapper/2.1/utils/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/2.1/utils/hal/Android.bp b/graphics/mapper/2.1/utils/hal/Android.bp
index 2a4cc6e..aff497c 100644
--- a/graphics/mapper/2.1/utils/hal/Android.bp
+++ b/graphics/mapper/2.1/utils/hal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.mapper@2.1-hal",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.1/utils/hal/include/mapper-hal/2.1/Mapper.h b/graphics/mapper/2.1/utils/hal/include/mapper-hal/2.1/Mapper.h
index 038f572..b4a2bedc 100644
--- a/graphics/mapper/2.1/utils/hal/include/mapper-hal/2.1/Mapper.h
+++ b/graphics/mapper/2.1/utils/hal/include/mapper-hal/2.1/Mapper.h
@@ -46,7 +46,7 @@
             return Error::BAD_BUFFER;
         }
 
-        return mHal->validateBufferSize(bufferHandle, descriptorInfo, stride);
+        return this->mHal->validateBufferSize(bufferHandle, descriptorInfo, stride);
     }
 
     Return<void> getTransportSize(void* buffer, IMapper::getTransportSize_cb hidl_cb) {
@@ -58,7 +58,7 @@
 
         uint32_t numFds = 0;
         uint32_t numInts = 0;
-        Error error = mHal->getTransportSize(bufferHandle, &numFds, &numInts);
+        Error error = this->mHal->getTransportSize(bufferHandle, &numFds, &numInts);
         hidl_cb(error, numFds, numInts);
         return Void();
     }
@@ -66,7 +66,7 @@
     Return<void> createDescriptor_2_1(const IMapper::BufferDescriptorInfo& descriptorInfo,
                                       IMapper::createDescriptor_2_1_cb hidl_cb) override {
         BufferDescriptor descriptor;
-        Error error = mHal->createDescriptor_2_1(descriptorInfo, &descriptor);
+        Error error = this->mHal->createDescriptor_2_1(descriptorInfo, &descriptor);
         hidl_cb(error, descriptor);
         return Void();
     }
@@ -74,7 +74,6 @@
    private:
     using BaseType2_0 = V2_0::hal::detail::MapperImpl<Interface, Hal>;
     using BaseType2_0::getImportedBuffer;
-    using BaseType2_0::mHal;
 };
 
 }  // namespace detail
diff --git a/graphics/mapper/2.1/utils/passthrough/Android.bp b/graphics/mapper/2.1/utils/passthrough/Android.bp
index 6946a53..d46041b 100644
--- a/graphics/mapper/2.1/utils/passthrough/Android.bp
+++ b/graphics/mapper/2.1/utils/passthrough/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.graphics.mapper@2.1-passthrough",
     defaults: ["hidl_defaults"],
diff --git a/graphics/mapper/2.1/utils/vts/Android.bp b/graphics/mapper/2.1/utils/vts/Android.bp
index abbe50a..5c67df9 100644
--- a/graphics/mapper/2.1/utils/vts/Android.bp
+++ b/graphics/mapper/2.1/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.mapper@2.1-vts",
     defaults: ["hidl_defaults", "VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/2.1/vts/OWNERS b/graphics/mapper/2.1/vts/OWNERS
index 11b7d21..4177296 100644
--- a/graphics/mapper/2.1/vts/OWNERS
+++ b/graphics/mapper/2.1/vts/OWNERS
@@ -1,7 +1,7 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
 
 # VTS team
 yim@google.com
diff --git a/graphics/mapper/2.1/vts/functional/Android.bp b/graphics/mapper/2.1/vts/functional/Android.bp
index ccbe40f..7bbc9a4 100644
--- a/graphics/mapper/2.1/vts/functional/Android.bp
+++ b/graphics/mapper/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsMapperV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
index 3d792f9..9f927e5 100644
--- a/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
+++ b/graphics/mapper/2.1/vts/functional/VtsHalGraphicsMapperV2_1TargetTest.cpp
@@ -204,6 +204,7 @@
     });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsMapperHidlTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, GraphicsMapperHidlTest,
         testing::Combine(
@@ -218,4 +219,4 @@
 }  // namespace mapper
 }  // namespace graphics
 }  // namespace hardware
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/graphics/mapper/3.0/Android.bp b/graphics/mapper/3.0/Android.bp
index a143243..401a3a2 100644
--- a/graphics/mapper/3.0/Android.bp
+++ b/graphics/mapper/3.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.mapper@3.0",
     root: "android.hardware",
diff --git a/graphics/mapper/3.0/utils/OWNERS b/graphics/mapper/3.0/utils/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/3.0/utils/OWNERS
+++ b/graphics/mapper/3.0/utils/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/3.0/utils/vts/Android.bp b/graphics/mapper/3.0/utils/vts/Android.bp
index c3d480a..c0d56de 100644
--- a/graphics/mapper/3.0/utils/vts/Android.bp
+++ b/graphics/mapper/3.0/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.mapper@3.0-vts",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/3.0/vts/OWNERS b/graphics/mapper/3.0/vts/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/3.0/vts/OWNERS
+++ b/graphics/mapper/3.0/vts/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/3.0/vts/functional/Android.bp b/graphics/mapper/3.0/vts/functional/Android.bp
index 3f4abec..e837027 100644
--- a/graphics/mapper/3.0/vts/functional/Android.bp
+++ b/graphics/mapper/3.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsMapperV3_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
index 92b5994..4187dd1 100644
--- a/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
+++ b/graphics/mapper/3.0/vts/functional/VtsHalGraphicsMapperV3_0TargetTest.cpp
@@ -458,6 +458,7 @@
     ASSERT_NO_FATAL_FAILURE(supported = mGralloc->isSupported(info));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsMapperHidlTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, GraphicsMapperHidlTest,
         testing::Combine(
diff --git a/graphics/mapper/4.0/Android.bp b/graphics/mapper/4.0/Android.bp
index 42c4942..4084dcd 100644
--- a/graphics/mapper/4.0/Android.bp
+++ b/graphics/mapper/4.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.graphics.mapper@4.0",
     root: "android.hardware",
diff --git a/graphics/mapper/4.0/utils/OWNERS b/graphics/mapper/4.0/utils/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/4.0/utils/OWNERS
+++ b/graphics/mapper/4.0/utils/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/4.0/utils/vts/Android.bp b/graphics/mapper/4.0/utils/vts/Android.bp
index 56ff116..6331498 100644
--- a/graphics/mapper/4.0/utils/vts/Android.bp
+++ b/graphics/mapper/4.0/utils/vts/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.graphics.mapper@4.0-vts",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/graphics/mapper/4.0/vts/OWNERS b/graphics/mapper/4.0/vts/OWNERS
index 2a56b38..c9f24d0 100644
--- a/graphics/mapper/4.0/vts/OWNERS
+++ b/graphics/mapper/4.0/vts/OWNERS
@@ -1,4 +1,4 @@
 # Graphics team
 chrisforbes@google.com
-stoza@google.com
-vhau@google.com
+jreck@google.com
+lpy@google.com
diff --git a/graphics/mapper/4.0/vts/functional/Android.bp b/graphics/mapper/4.0/vts/functional/Android.bp
index 03abc89..11ebdc5 100644
--- a/graphics/mapper/4.0/vts/functional/Android.bp
+++ b/graphics/mapper/4.0/vts/functional/Android.bp
@@ -14,12 +14,21 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalGraphicsMapperV4_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: ["VtsHalGraphicsMapperV4_0TargetTest.cpp"],
     static_libs: [
-        "android.hardware.graphics.common-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk_platform",
         "android.hardware.graphics.mapper@4.0-vts",
         "libgralloctypes",
         "libsync",
diff --git a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
index bb775dc..f55a6b7 100644
--- a/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
+++ b/graphics/mapper/4.0/vts/functional/VtsHalGraphicsMapperV4_0TargetTest.cpp
@@ -2592,6 +2592,7 @@
     ASSERT_EQ(0, reservedSize);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GraphicsMapperHidlTest);
 INSTANTIATE_TEST_CASE_P(
         PerInstance, GraphicsMapperHidlTest,
         testing::Combine(
diff --git a/health/1.0/Android.bp b/health/1.0/Android.bp
index ea6b0c8..003b106 100644
--- a/health/1.0/Android.bp
+++ b/health/1.0/Android.bp
@@ -1,14 +1,19 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.health@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
-        "IHealth.hal",
     ],
     interfaces: [
         "android.hidl.base@1.0",
diff --git a/health/1.0/IHealth.hal b/health/1.0/IHealth.hal
deleted file mode 100644
index 3828589..0000000
--- a/health/1.0/IHealth.hal
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-package android.hardware.health@1.0;
-
-interface IHealth {
-    /**
-     * This function lets you change healthd configuration from default if
-     * desired. It must be called exactly once at startup time.
-     *
-     * The configuration values are described in 'struct HealthConfig'.
-     * To use default configuration, simply return without modifying the
-     * fields of the config parameter.
-     *
-     * @param default healthd configuration.
-     */
-    init(HealthConfig config) generates (HealthConfig configOut);
-
-    /**
-     * This function is a hook to update/change device's HealthInfo (as described
-     * in 'struct HealthInfo').
-     *
-     * 'HealthInfo' describes device's battery and charging status, typically
-     * read from kernel. These values may be modified in this call.
-     *
-     * @param   Device Health info as described in 'struct HealthInfo'.
-     * @return  skipLogging Indication to the caller to add 'or' skip logging the health
-     *          information. Return 'true' to skip logging the update.
-     * @return  infoOut HealthInfo to be sent to client code. (May or may
-     *          not be modified).
-     */
-    update(HealthInfo info) generates (bool skipLogging, HealthInfo infoOut);
-
-    /**
-     * This function is called by healthd when framework queries for remaining
-     * energy in the Battery through BatteryManager APIs.
-     *
-     * @return  result Result of querying enery counter for the battery.
-     * @return  energy Battery remaining energy in nanowatt-hours.
-     *          Must be '0' if result is anything other than Result::SUCCESS.
-     */
-    energyCounter() generates (Result result, int64_t energy);
-};
diff --git a/health/1.0/default/Android.bp b/health/1.0/default/Android.bp
index 7581335..6a21595 100644
--- a/health/1.0/default/Android.bp
+++ b/health/1.0/default/Android.bp
@@ -1,10 +1,19 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.health@1.0-convert",
     vendor_available: true,
     recovery_available: true,
     srcs: ["convert.cpp"],
     include_dirs: [
-        "system/core/base/include",
+        "system/libbase/include",
     ],
     header_libs: ["libhealthd_headers"],
     export_header_lib_headers: ["libhealthd_headers"],
@@ -17,56 +26,3 @@
     ],
 
 }
-
-cc_library_static {
-    name: "android.hardware.health@1.0-impl-helper",
-    vendor: true,
-    srcs: ["Health.cpp"],
-
-    header_libs: [
-        "libbase_headers",
-        "libhealthd_headers",
-    ],
-
-    shared_libs: [
-        "libcutils",
-        "libhidlbase",
-        "liblog",
-        "libutils",
-        "android.hardware.health@1.0",
-    ],
-
-    static_libs: [
-        "android.hardware.health@1.0-convert",
-    ],
-}
-
-cc_library_shared {
-    name: "android.hardware.health@1.0-impl",
-    vendor: true,
-    relative_install_path: "hw",
-
-    static_libs: [
-        "android.hardware.health@1.0-impl-helper",
-        "android.hardware.health@1.0-convert",
-        "libhealthd.default",
-    ],
-}
-
-cc_binary {
-    name: "android.hardware.health@1.0-service",
-    vendor: true,
-    relative_install_path: "hw",
-    init_rc: ["android.hardware.health@1.0-service.rc"],
-    srcs: ["HealthService.cpp"],
-
-    shared_libs: [
-        "liblog",
-        "libcutils",
-        "libdl",
-        "libbase",
-        "libutils",
-        "libhidlbase",
-        "android.hardware.health@1.0",
-    ],
-}
diff --git a/health/1.0/default/Health.cpp b/health/1.0/default/Health.cpp
deleted file mode 100644
index 1a02956..0000000
--- a/health/1.0/default/Health.cpp
+++ /dev/null
@@ -1,93 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-#define LOG_TAG "health-hal"
-
-#include <Health.h>
-#include <include/hal_conversion.h>
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthConfig;
-using ::android::hardware::health::V1_0::hal_conversion::convertToHealthInfo;
-using ::android::hardware::health::V1_0::hal_conversion::convertFromHealthInfo;
-
-// Methods from ::android::hardware::health::V1_0::IHealth follow.
-Return<void> Health::init(const HealthConfig& config, init_cb _hidl_cb)  {
-    struct healthd_config healthd_config = {};
-    HealthConfig configOut;
-
-    // To keep working with existing healthd static HALs,
-    // convert the new HealthConfig to the old healthd_config
-    // and back.
-
-    convertFromHealthConfig(config, &healthd_config);
-    healthd_board_init(&healthd_config);
-    mGetEnergyCounter = healthd_config.energyCounter;
-    convertToHealthConfig(&healthd_config, configOut);
-
-    _hidl_cb(configOut);
-
-    return Void();
-}
-
-Return<void> Health::update(const HealthInfo& info, update_cb _hidl_cb)  {
-    struct android::BatteryProperties p = {};
-    HealthInfo infoOut;
-
-    // To keep working with existing healthd static HALs,
-    // convert the new HealthInfo to android::Batteryproperties
-    // and back.
-
-    convertFromHealthInfo(info, &p);
-    int skipLogging = healthd_board_battery_update(&p);
-    convertToHealthInfo(&p, infoOut);
-
-    _hidl_cb(!!skipLogging, infoOut);
-
-    return Void();
-}
-
-Return<void> Health::energyCounter(energyCounter_cb _hidl_cb) {
-    int64_t energy = 0;
-    Result result = Result::NOT_SUPPORTED;
-
-    if (mGetEnergyCounter) {
-        int status = mGetEnergyCounter(&energy);
-        if (status == 0) {
-            result = Result::SUCCESS;
-        }
-    }
-
-    _hidl_cb(result, energy);
-
-   return Void();
-}
-
-IHealth* HIDL_FETCH_IHealth(const char* /* name */) {
-    return new Health();
-}
-
-} // namespace implementation
-}  // namespace V1_0
-}  // namespace health
-}  // namespace hardware
-}  // namespace android
diff --git a/health/1.0/default/Health.h b/health/1.0/default/Health.h
deleted file mode 100644
index ed364c1..0000000
--- a/health/1.0/default/Health.h
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2016 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_HEALTH_V1_0_HEALTH_H
-#define ANDROID_HARDWARE_HEALTH_V1_0_HEALTH_H
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <hidl/Status.h>
-#include <hidl/MQDescriptor.h>
-#include <healthd/healthd.h>
-#include <utils/String8.h>
-
-namespace android {
-namespace hardware {
-namespace health {
-namespace V1_0 {
-namespace implementation {
-
-using ::android::hardware::health::V1_0::HealthInfo;
-using ::android::hardware::health::V1_0::HealthConfig;
-using ::android::hardware::health::V1_0::IHealth;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::hidl_string;
-using ::android::sp;
-
-struct Health : public IHealth {
-    // Methods from ::android::hardware::health::V1_0::IHealth follow.
-    Return<void> init(const HealthConfig& config, init_cb _hidl_cb)  override;
-    Return<void> update(const HealthInfo& info, update_cb _hidl_cb)  override;
-    Return<void> energyCounter(energyCounter_cb _hidl_cb) override;
-private:
-    std::function<int(int64_t *)> mGetEnergyCounter;
-};
-
-extern "C" IHealth* HIDL_FETCH_IHealth(const char* name);
-
-}  // namespace implementation
-}  // namespace V1_0
-}  // namespace health
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_HEALTH_V1_0_HEALTH_H
diff --git a/health/1.0/default/HealthService.cpp b/health/1.0/default/HealthService.cpp
deleted file mode 100644
index 55848d2..0000000
--- a/health/1.0/default/HealthService.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright 2016 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.
- */
-
-#define LOG_TAG "android.hardware.health@1.0-service"
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <hidl/LegacySupport.h>
-
-using android::hardware::health::V1_0::IHealth;
-using android::hardware::defaultPassthroughServiceImplementation;
-
-int main() {
-    return defaultPassthroughServiceImplementation<IHealth>();
-}
diff --git a/health/1.0/default/README.md b/health/1.0/default/README.md
deleted file mode 100644
index 1ded7de..0000000
--- a/health/1.0/default/README.md
+++ /dev/null
@@ -1,66 +0,0 @@
-# Implement the 2.1 HAL instead!
-
-It is strongly recommended that you implement the 2.1 HAL directly. See
-`hardware/interfaces/health/2.1/README.md` for more details.
-
-# Implement Health 1.0 HAL
-
-1. Install common binderized service. The binderized service `dlopen()`s
-   passthrough implementations on the device, so there is no need to write
-   your own.
-
-    ```mk
-    # Install default binderized implementation to vendor.
-    PRODUCT_PACKAGES += android.hardware.health@1.0-service
-    ```
-
-1. Add proper VINTF manifest entry to your device manifest. Example:
-
-    ```xml
-    <hal format="hidl">
-        <name>android.hardware.health</name>
-        <transport>hwbinder</transport>
-        <version>1.0</version>
-        <interface>
-            <name>IHealth</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-    ```
-
-1. Install the proper passthrough implemetation.
-
-    1. If you want to use the default implementation (with default `libhealthd`),
-       add the following to `device.mk`:
-
-        ```mk
-        PRODUCT_PACKAGES += \
-            android.hardware.health@1.0-impl
-        ```
-
-    1. Otherwise, if you have a customized `libhealthd.<board>`:
-
-        1. Define your passthrough implementation. Example (replace `<device>`
-           and `<board>` accordingly):
-
-            ```bp
-            cc_library_shared {
-                name: "android.hardware.health@1.0-impl-<device>",
-                vendor: true,
-                relative_install_path: "hw",
-
-                static_libs: [
-                    "android.hardware.health@1.0-impl-helper",
-                    "android.hardware.health@1.0-convert",
-                    "libhealthd.<board>",
-                ],
-            }
-            ```
-
-        1. Add to `device.mk`.
-
-            ```
-            PRODUCT_PACKAGES += android.hardware.health@1.0-impl-<device>
-            ```
-
-        1. Define appropriate SELinux permissions.
diff --git a/health/1.0/default/android.hardware.health@1.0-service.rc b/health/1.0/default/android.hardware.health@1.0-service.rc
deleted file mode 100644
index 405784f..0000000
--- a/health/1.0/default/android.hardware.health@1.0-service.rc
+++ /dev/null
@@ -1,5 +0,0 @@
-service vendor.health-hal-1-0 /vendor/bin/hw/android.hardware.health@1.0-service
-    class hal
-    user system
-    group system
-    capabilities WAKE_ALARM
diff --git a/health/1.0/default/convert.cpp b/health/1.0/default/convert.cpp
index 7f1e3c4..3680d4d 100644
--- a/health/1.0/default/convert.cpp
+++ b/health/1.0/default/convert.cpp
@@ -79,7 +79,7 @@
 
     hc->batteryCurrentAvgPath =
         android::String8(c.batteryCurrentAvgPath.c_str(),
-                         c.batteryCurrentNowPath.size());
+                         c.batteryCurrentAvgPath.size());
 
     hc->batteryChargeCounterPath =
         android::String8(c.batteryChargeCounterPath.c_str(),
diff --git a/health/1.0/default/include/hal_conversion.h b/health/1.0/default/include/hal_conversion.h
index a92b208..a8ddb73 100644
--- a/health/1.0/default/include/hal_conversion.h
+++ b/health/1.0/default/include/hal_conversion.h
@@ -17,7 +17,7 @@
 #ifndef HARDWARE_INTERFACES_HEALTH_V1_0_DEFAULT_INCLUDE_HAL_CONVERSION_H_
 #define HARDWARE_INTERFACES_HEALTH_V1_0_DEFAULT_INCLUDE_HAL_CONVERSION_H_
 
-#include <android/hardware/health/1.0/IHealth.h>
+#include <android/hardware/health/1.0/types.h>
 #include <healthd/healthd.h>
 
 namespace android {
diff --git a/health/1.0/default/libhealthd/Android.bp b/health/1.0/default/libhealthd/Android.bp
deleted file mode 100644
index a90d50d..0000000
--- a/health/1.0/default/libhealthd/Android.bp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Copyright 2016 The Android Open Source Project
-
-cc_library_static {
-    srcs: ["healthd_board_default.cpp"],
-    name: "libhealthd.default",
-    vendor_available: true,
-    recovery_available: true,
-    cflags: ["-Werror"],
-    include_dirs: ["system/core/base/include"],
-    header_libs: ["libhealthd_headers"],
-}
diff --git a/health/1.0/default/libhealthd/healthd_board_default.cpp b/health/1.0/default/libhealthd/healthd_board_default.cpp
deleted file mode 100644
index 127f98e..0000000
--- a/health/1.0/default/libhealthd/healthd_board_default.cpp
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * Copyright (C) 2013 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 <healthd/healthd.h>
-
-void healthd_board_init(struct healthd_config*)
-{
-    // use defaults
-}
-
-int healthd_board_battery_update(struct android::BatteryProperties*)
-{
-    // return 0 to log periodic polled battery status to kernel log
-    return 0;
-}
diff --git a/health/1.0/vts/functional/Android.bp b/health/1.0/vts/functional/Android.bp
deleted file mode 100644
index f4a04a7..0000000
--- a/health/1.0/vts/functional/Android.bp
+++ /dev/null
@@ -1,23 +0,0 @@
-//
-// Copyright (C) 2017 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_test {
-    name: "VtsHalHealthV1_0TargetTest",
-    defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["VtsHalHealthV1_0TargetTest.cpp"],
-    static_libs: ["android.hardware.health@1.0"],
-    test_suites: ["general-tests", "vts"],
-}
diff --git a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp b/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
deleted file mode 100644
index b985a9f..0000000
--- a/health/1.0/vts/functional/VtsHalHealthV1_0TargetTest.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "health_hidl_hal_test"
-
-#include <android/hardware/health/1.0/IHealth.h>
-#include <android/hardware/health/1.0/types.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <log/log.h>
-
-using HealthConfig = ::android::hardware::health::V1_0::HealthConfig;
-using HealthInfo = ::android::hardware::health::V1_0::HealthInfo;
-using IHealth = ::android::hardware::health::V1_0::IHealth;
-using Result = ::android::hardware::health::V1_0::Result;
-
-using ::android::sp;
-
-class HealthHidlTest : public ::testing::TestWithParam<std::string> {
-   public:
-    virtual void SetUp() override {
-        health = IHealth::getService(GetParam());
-        ASSERT_NE(health, nullptr);
-        health->init(config,
-                     [&](const auto& halConfigOut) { config = halConfigOut; });
-    }
-
-    sp<IHealth> health;
-    HealthConfig config;
-};
-
-/**
- * Ensure EnergyCounter call returns positive energy counter or NOT_SUPPORTED
- */
-TEST_P(HealthHidlTest, TestEnergyCounter) {
-    Result result;
-    int64_t energy = 0;
-    health->energyCounter([&](Result ret, int64_t energyOut) {
-        result = ret;
-        energy = energyOut;
-    });
-
-    ASSERT_TRUE(result == Result::SUCCESS || result == Result::NOT_SUPPORTED);
-    ASSERT_TRUE(result != Result::SUCCESS || energy > 0);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-        PerInstance, HealthHidlTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
-        android::hardware::PrintInstanceNameToString);
diff --git a/health/2.0/Android.bp b/health/2.0/Android.bp
index b8323b6..ddd983d 100644
--- a/health/2.0/Android.bp
+++ b/health/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.health@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IHealth.hal",
diff --git a/health/2.0/default/Android.bp b/health/2.0/default/Android.bp
index 3c5877e..73cd553 100644
--- a/health/2.0/default/Android.bp
+++ b/health/2.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.health@2.0-impl_defaults",
 
diff --git a/health/2.0/utils/libhealthhalutils/Android.bp b/health/2.0/utils/libhealthhalutils/Android.bp
index 449ef16..fa6e67e 100644
--- a/health/2.0/utils/libhealthhalutils/Android.bp
+++ b/health/2.0/utils/libhealthhalutils/Android.bp
@@ -16,6 +16,15 @@
 
 // Convenience library for (hwbinder) clients to choose the correct health
 // service instance.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libhealthhalutils",
     srcs: ["HealthHalUtils.cpp"],
diff --git a/health/2.0/utils/libhealthservice/Android.bp b/health/2.0/utils/libhealthservice/Android.bp
index 88c38f2..8023692 100644
--- a/health/2.0/utils/libhealthservice/Android.bp
+++ b/health/2.0/utils/libhealthservice/Android.bp
@@ -3,6 +3,15 @@
 // libhealthd and libhealthstoragedefault.
 
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libhealthservice",
     vendor_available: true,
diff --git a/health/2.0/utils/libhealthstoragedefault/Android.bp b/health/2.0/utils/libhealthstoragedefault/Android.bp
index 5bc8b9f..3de8789 100644
--- a/health/2.0/utils/libhealthstoragedefault/Android.bp
+++ b/health/2.0/utils/libhealthstoragedefault/Android.bp
@@ -17,6 +17,15 @@
 // Default implementation for (passthrough) clients that statically links to
 // android.hardware.health@2.0-impl but do no query for storage related
 // information.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     srcs: ["StorageHealthDefault.cpp"],
     name: "libhealthstoragedefault",
diff --git a/health/2.0/vts/functional/Android.bp b/health/2.0/vts/functional/Android.bp
index ab14ca7..eb69612 100644
--- a/health/2.0/vts/functional/Android.bp
+++ b/health/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalHealthV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
index 441e2d7..7fabf2b 100644
--- a/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
+++ b/health/2.0/vts/functional/VtsHalHealthV2_0TargetTest.cpp
@@ -319,6 +319,7 @@
     }));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, HealthHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
@@ -669,6 +670,7 @@
             << "You may want to try again later when getHealthInfo becomes stable.";
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(BatteryTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, BatteryTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
diff --git a/health/2.1/Android.bp b/health/2.1/Android.bp
index 254bfc0..8a11617 100644
--- a/health/2.1/Android.bp
+++ b/health/2.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.health@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IHealth.hal",
diff --git a/health/2.1/default/Android.bp b/health/2.1/default/Android.bp
index 3649853..b7bcea5 100644
--- a/health/2.1/default/Android.bp
+++ b/health/2.1/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.health@2.1-impl-defaults",
     relative_install_path: "hw",
diff --git a/health/2.1/default/android.hardware.health@2.1-service.rc b/health/2.1/default/android.hardware.health@2.1-service.rc
index b6d9e3b..8728257 100644
--- a/health/2.1/default/android.hardware.health@2.1-service.rc
+++ b/health/2.1/default/android.hardware.health@2.1-service.rc
@@ -2,5 +2,5 @@
     class hal charger
     user system
     group system
-    capabilities WAKE_ALARM
+    capabilities WAKE_ALARM BLOCK_SUSPEND
     file /dev/kmsg w
diff --git a/health/2.1/types.hal b/health/2.1/types.hal
index d775491..6d7b661 100644
--- a/health/2.1/types.hal
+++ b/health/2.1/types.hal
@@ -98,9 +98,8 @@
     /**
      * Estimated battery full charge design capacity (in microamp hours, uAh).
      * Value must be 0 if unknown.
-     * Value must be positive if known.
-     * Value must be greater than 100 000 uAh.
-     * Value must be less than 100 000 000 uAh.
+     * Value must be greater than 100 000 uAh if known.
+     * Value must be less than 100 000 000 uAh if known.
      */
     int32_t batteryFullChargeDesignCapacityUah;
 };
diff --git a/health/2.1/vts/functional/Android.bp b/health/2.1/vts/functional/Android.bp
index 7894ca2..238a150 100644
--- a/health/2.1/vts/functional/Android.bp
+++ b/health/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalHealthV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
index deb1a29..4ae443a 100644
--- a/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
+++ b/health/2.1/vts/functional/VtsHalHealthV2_1TargetTest.cpp
@@ -233,16 +233,17 @@
         ASSERT_EQ(Result::SUCCESS, result);
 
         EXPECT_TRUE(IsEnum(value.batteryCapacityLevel)) << " BatteryCapacityLevel";
-        EXPECT_GE(value.batteryChargeTimeToFullNowSeconds, 0);
+        EXPECT_GE(value.batteryChargeTimeToFullNowSeconds, -1);
 
-        EXPECT_GE(value.batteryFullChargeDesignCapacityUah, 0)
-                << "batteryFullChargeDesignCapacityUah should not be negative";
+        if (value.batteryFullChargeDesignCapacityUah != 0) {
+            EXPECT_GT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MIN)
+                    << "batteryFullChargeDesignCapacityUah should be greater than 100 mAh, or 0 if "
+                       "unknown";
 
-        EXPECT_GT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MIN)
-                << "batteryFullChargeDesignCapacityUah should be greater than 100 mAh";
-
-        EXPECT_LT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MAX)
-                << "batteryFullChargeDesignCapacityUah should be less than 100,000 mAh";
+            EXPECT_LT((long)value.batteryFullChargeDesignCapacityUah, FULL_CHARGE_DESIGN_CAP_MAX)
+                    << "batteryFullChargeDesignCapacityUah should be less than 100,000 mAh, or 0 "
+                       "if unknown";
+        }
     })));
 }
 
@@ -258,6 +259,7 @@
     })));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, HealthHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHealth::descriptor)),
diff --git a/health/storage/1.0/Android.bp b/health/storage/1.0/Android.bp
index e4620f8..70b2bdc 100644
--- a/health/storage/1.0/Android.bp
+++ b/health/storage/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.health.storage@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IGarbageCollectCallback.hal",
diff --git a/health/storage/1.0/default/Android.bp b/health/storage/1.0/default/Android.bp
index 3156dfe..7edf9d1 100644
--- a/health/storage/1.0/default/Android.bp
+++ b/health/storage/1.0/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.health.storage@1.0-service",
     vendor: true,
@@ -38,6 +47,7 @@
     ],
 
     static_libs: [
+        "libhealth_storage_impl_common",
         "libfstab",
     ],
 
diff --git a/health/storage/1.0/default/Storage.cpp b/health/storage/1.0/default/Storage.cpp
index 561deaa..02b6a3d 100644
--- a/health/storage/1.0/default/Storage.cpp
+++ b/health/storage/1.0/default/Storage.cpp
@@ -18,11 +18,8 @@
 
 #include <sstream>
 
-#include <android-base/chrono_utils.h>
-#include <android-base/file.h>
 #include <android-base/logging.h>
-#include <android-base/strings.h>
-#include <fstab/fstab.h>
+#include <health-storage-impl/common.h>
 
 namespace android {
 namespace hardware {
@@ -31,69 +28,9 @@
 namespace V1_0 {
 namespace implementation {
 
-using base::ReadFileToString;
-using base::Timer;
-using base::Trim;
-using base::WriteStringToFd;
-using base::WriteStringToFile;
-using fs_mgr::Fstab;
-using fs_mgr::ReadDefaultFstab;
-
-std::string getGarbageCollectPath() {
-    Fstab fstab;
-    ReadDefaultFstab(&fstab);
-
-    for (const auto& entry : fstab) {
-        if (!entry.sysfs_path.empty()) {
-            return entry.sysfs_path + "/manual_gc";
-        }
-    }
-
-    return "";
-}
-
 Return<void> Storage::garbageCollect(uint64_t timeoutSeconds,
                                      const sp<IGarbageCollectCallback>& cb) {
-    Result result = Result::SUCCESS;
-    std::string path = getGarbageCollectPath();
-
-    if (path.empty()) {
-        LOG(WARNING) << "Cannot find Dev GC path";
-        result = Result::UNKNOWN_ERROR;
-    } else {
-        Timer timer;
-        LOG(INFO) << "Start Dev GC on " << path;
-        while (1) {
-            std::string require;
-            if (!ReadFileToString(path, &require)) {
-                PLOG(WARNING) << "Reading manual_gc failed in " << path;
-                result = Result::IO_ERROR;
-                break;
-            }
-            require = Trim(require);
-            if (require == "" || require == "off" || require == "disabled") {
-                LOG(DEBUG) << "No more to do Dev GC";
-                break;
-            }
-            LOG(DEBUG) << "Trigger Dev GC on " << path;
-            if (!WriteStringToFile("1", path)) {
-                PLOG(WARNING) << "Start Dev GC failed on " << path;
-                result = Result::IO_ERROR;
-                break;
-            }
-            if (timer.duration() >= std::chrono::seconds(timeoutSeconds)) {
-                LOG(WARNING) << "Dev GC timeout";
-                // Timeout is not treated as an error. Try next time.
-                break;
-            }
-            sleep(2);
-        }
-        LOG(INFO) << "Stop Dev GC on " << path;
-        if (!WriteStringToFile("0", path)) {
-            PLOG(WARNING) << "Stop Dev GC failed on " << path;
-            result = Result::IO_ERROR;
-        }
-    }
+    Result result = GarbageCollect(timeoutSeconds);
 
     if (cb != nullptr) {
         auto ret = cb->onFinish(result);
@@ -110,28 +47,7 @@
     }
 
     int fd = handle->data[0];
-    std::stringstream output;
-
-    std::string path = getGarbageCollectPath();
-    if (path.empty()) {
-        output << "Cannot find Dev GC path";
-    } else {
-        std::string require;
-
-        if (ReadFileToString(path, &require)) {
-            output << path << ":" << require << std::endl;
-        }
-
-        if (WriteStringToFile("0", path)) {
-            output << "stop success" << std::endl;
-        }
-    }
-
-    if (!WriteStringToFd(output.str(), fd)) {
-        PLOG(WARNING) << "debug: cannot write to fd";
-    }
-
-    fsync(fd);
+    DebugDump(fd);
 
     return Void();
 }
diff --git a/health/storage/1.0/vts/functional/Android.bp b/health/storage/1.0/vts/functional/Android.bp
index 2201031..ccf22ce 100644
--- a/health/storage/1.0/vts/functional/Android.bp
+++ b/health/storage/1.0/vts/functional/Android.bp
@@ -14,11 +14,23 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalHealthStorageV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: ["VtsHalHealthStorageV1_0TargetTest.cpp"],
     static_libs: ["android.hardware.health.storage@1.0"],
+    header_libs: [
+        "libhealth_storage_test_common_headers",
+    ],
     shared_libs: [
         "libhidlbase",
     ],
diff --git a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
index eaa44ec..ddb6b5a 100644
--- a/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
+++ b/health/storage/1.0/vts/functional/VtsHalHealthStorageV1_0TargetTest.cpp
@@ -14,14 +14,17 @@
  * limitations under the License.
  */
 
+#include <unistd.h>
+
+#include <thread>
+
 #include <android-base/logging.h>
 #include <android/hardware/health/storage/1.0/IStorage.h>
 #include <gtest/gtest.h>
+#include <health-storage-test/common.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/HidlTransportSupport.h>
 #include <hidl/ServiceManagement.h>
-#include <unistd.h>
-#include <thread>
 
 namespace android {
 namespace hardware {
@@ -29,61 +32,17 @@
 namespace storage {
 namespace V1_0 {
 
+using namespace ::android::hardware::health::storage::test;
 using ::std::literals::chrono_literals::operator""ms;
 
 #define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.description()
 
-// Dev GC timeout. This is the timeout used by vold.
-const uint64_t kDevGcTimeoutSec = 120;
-const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
-// Dev GC timeout tolerance. The HAL may not immediately return after the
-// timeout, so include an acceptable tolerance.
-const std::chrono::seconds kDevGcTolerance{3};
-// Time accounted for RPC calls.
-const std::chrono::milliseconds kRpcTime{1000};
-
-template <typename R>
-std::string toString(std::chrono::duration<R, std::milli> time) {
-    return std::to_string(time.count()) + "ms";
-}
-
-/** An atomic boolean flag that indicates whether a task has finished. */
-class Flag {
-   public:
-    void onFinish() {
-        std::unique_lock<std::mutex> lock(mMutex);
-        onFinishLocked(&lock);
-    }
-    template <typename R, typename P>
-    bool wait(std::chrono::duration<R, P> duration) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        return waitLocked(&lock, duration);
-    }
-
-   protected:
-    /** Will unlock. */
-    void onFinishLocked(std::unique_lock<std::mutex>* lock) {
-        mFinished = true;
-        lock->unlock();
-        mCv.notify_all();
-    }
-    template <typename R, typename P>
-    bool waitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
-        mCv.wait_for(*lock, duration, [this] { return mFinished; });
-        return mFinished;
-    }
-
-    bool mFinished{false};
-    std::mutex mMutex;
-    std::condition_variable mCv;
-};
-
 class GcCallback : public IGarbageCollectCallback, public Flag {
-   public:
+  public:
     Return<void> onFinish(Result result) override {
-        std::unique_lock<std::mutex> lock(mMutex);
-        mResult = result;
-        Flag::onFinishLocked(&lock);
+        std::unique_lock<std::mutex> lock(mutex_);
+        result_ = result;
+        Flag::OnFinishLocked(&lock);
         return Void();
     }
 
@@ -93,13 +52,13 @@
      */
     template <typename R, typename P>
     void waitForResult(std::chrono::duration<R, P> timeout, Result expected) {
-        std::unique_lock<std::mutex> lock(mMutex);
-        ASSERT_TRUE(waitLocked(&lock, timeout)) << "timeout after " << toString(timeout);
-        EXPECT_EQ(expected, mResult);
+        std::unique_lock<std::mutex> lock(mutex_);
+        ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
+        EXPECT_EQ(expected, result_);
     }
 
-   private:
-    Result mResult{Result::UNKNOWN_ERROR};
+  private:
+    Result result_{Result::UNKNOWN_ERROR};
 };
 
 class HealthStorageHidlTest : public ::testing::TestWithParam<std::string> {
@@ -127,10 +86,10 @@
         auto pingFlag = std::make_shared<Flag>();
         std::thread([service, pingFlag] {
             service->ping();
-            pingFlag->onFinish();
+            pingFlag->OnFinish();
         })
             .detach();
-        return pingFlag->wait(timeout);
+        return pingFlag->Wait(timeout);
     }
 
     sp<IStorage> fs;
@@ -147,7 +106,7 @@
     // Hold test process because HAL can be single-threaded and doing GC.
     ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
             << "Service must be available after "
-            << toString(kDevGcTimeout + kDevGcTolerance + kRpcTime);
+            << to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
 }
 
 /**
@@ -160,6 +119,7 @@
     cb->waitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthStorageHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, HealthStorageHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IStorage::descriptor)),
diff --git a/health/storage/aidl/Android.bp b/health/storage/aidl/Android.bp
new file mode 100644
index 0000000..bf49466
--- /dev/null
+++ b/health/storage/aidl/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.health.storage",
+    vendor_available: true,
+    srcs: ["android/hardware/health/storage/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            enabled: false,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl
new file mode 100644
index 0000000..0f382d7
--- /dev/null
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IGarbageCollectCallback.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.health.storage;
+@VintfStability
+interface IGarbageCollectCallback {
+  oneway void onFinish(in android.hardware.health.storage.Result result);
+}
diff --git a/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl
new file mode 100644
index 0000000..61f838a
--- /dev/null
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/IStorage.aidl
@@ -0,0 +1,23 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.health.storage;
+@VintfStability
+interface IStorage {
+  oneway void garbageCollect(in long timeoutSeconds, in android.hardware.health.storage.IGarbageCollectCallback callback);
+}
diff --git a/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl
new file mode 100644
index 0000000..a345808
--- /dev/null
+++ b/health/storage/aidl/aidl_api/android.hardware.health.storage/current/android/hardware/health/storage/Result.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.health.storage;
+@Backing(type="int") @VintfStability
+enum Result {
+  SUCCESS = 0,
+  IO_ERROR = 1,
+  UNKNOWN_ERROR = 2,
+}
diff --git a/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl b/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl
new file mode 100644
index 0000000..ccd1b44
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/IGarbageCollectCallback.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.
+ */
+
+package android.hardware.health.storage;
+
+import android.hardware.health.storage.Result;
+
+/**
+ * Callback interface to IStorage.garbageCollect.
+ */
+@VintfStability
+interface IGarbageCollectCallback {
+    /**
+     * When garbage collection has finished, the implementation must
+     * invoke this function to indicate the result of the garbage collection.
+     *
+     * @param out result Execution result. See documentation for Result for
+     *     details.
+     */
+    oneway void onFinish(in Result result);
+}
diff --git a/health/storage/aidl/android/hardware/health/storage/IStorage.aidl b/health/storage/aidl/android/hardware/health/storage/IStorage.aidl
new file mode 100644
index 0000000..78992a2
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/IStorage.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.hardware.health.storage;
+
+import android.hardware.health.storage.IGarbageCollectCallback;
+
+/**
+ * IStorage is an interface that provides operations on underlying storage
+ * devices, including flash memory.
+ */
+@VintfStability
+interface IStorage {
+    /**
+     * Start garbage collection on the driver of storage devices.
+     *
+     * Garbage collection must be started at regular intervals when it is a good
+     * time for a longer-running cleanup tasks, roughly daily.
+     *
+     * When garbage collection finishes or encounters an error before the
+     * specified timeout, the implementation must call IGarbageCollect.finish
+     * immediately with appropriate result.
+     *
+     * If garbage collection does not finish within the specified timeout,
+     * the implementation must stop garbage collection, and must not call
+     * IGarbageCollect.finish.
+     *
+     * @param timeoutSeconds timeout in seconds. The implementation must
+     *     return after the timeout is reached.
+     *
+     * @param callback callback interface. Callback must be null if the client
+     *     does not need to receive any callbacks.
+     *
+     */
+    oneway void garbageCollect(in long timeoutSeconds, in IGarbageCollectCallback callback);
+}
diff --git a/health/storage/aidl/android/hardware/health/storage/Result.aidl b/health/storage/aidl/android/hardware/health/storage/Result.aidl
new file mode 100644
index 0000000..73bb779
--- /dev/null
+++ b/health/storage/aidl/android/hardware/health/storage/Result.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.hardware.health.storage;
+
+/**
+ * Status values for HAL methods.
+ */
+@VintfStability
+@Backing(type="int")
+enum Result {
+    /**
+     * Execution of the method is successful.
+     */
+    SUCCESS = 0,
+    /**
+     * An IO error is encountered when the HAL communicates with the device.
+     */
+    IO_ERROR,
+    /**
+     * An unknown error is encountered.
+     */
+    UNKNOWN_ERROR,
+}
diff --git a/health/storage/aidl/default/Android.bp b/health/storage/aidl/default/Android.bp
new file mode 100644
index 0000000..819b885
--- /dev/null
+++ b/health/storage/aidl/default/Android.bp
@@ -0,0 +1,62 @@
+/*
+ * 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.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "libhealth_storage_impl_defaults",
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.health.storage-V1-ndk_platform",
+    ],
+    static_libs: [
+        "libfstab",
+        "libhealth_storage_impl_common",
+    ],
+}
+
+cc_library_static {
+    name: "libhealth_storage_default_impl",
+    defaults: ["libhealth_storage_impl_defaults"],
+    srcs: [
+        "Storage.cpp",
+    ],
+    visibility: [
+        ":__subpackages__",
+        "//hardware/interfaces/tests/extension/health/storage:__subpackages__",
+    ],
+}
+
+cc_binary {
+    name: "android.hardware.health.storage-service.default",
+    defaults: ["libhealth_storage_impl_defaults"],
+    relative_install_path: "hw",
+    init_rc: ["health-storage-default.rc"],
+    vintf_fragments: ["health-storage-default.xml"],
+    srcs: ["main.cpp"],
+    static_libs: [
+        "libhealth_storage_default_impl",
+    ],
+}
diff --git a/health/storage/aidl/default/Storage.cpp b/health/storage/aidl/default/Storage.cpp
new file mode 100644
index 0000000..faa4ff6
--- /dev/null
+++ b/health/storage/aidl/default/Storage.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include "Storage.h"
+
+#include <sstream>
+
+#include <android-base/logging.h>
+#include <health-storage-impl/common.h>
+
+using ::android::hardware::health::storage::DebugDump;
+using ::android::hardware::health::storage::GarbageCollect;
+
+using HResult = android::hardware::health::storage::V1_0::Result;
+using AResult = aidl::android::hardware::health::storage::Result;
+// Ensure static_cast<AResult>(any HResult) works
+static_assert(static_cast<AResult>(HResult::SUCCESS) == AResult::SUCCESS);
+static_assert(static_cast<AResult>(HResult::IO_ERROR) == AResult::IO_ERROR);
+static_assert(static_cast<AResult>(HResult::UNKNOWN_ERROR) == AResult::UNKNOWN_ERROR);
+
+namespace aidl::android::hardware::health::storage {
+
+ndk::ScopedAStatus Storage::garbageCollect(
+        int64_t timeout_seconds, const std::shared_ptr<IGarbageCollectCallback>& callback) {
+    AResult result = static_cast<AResult>(GarbageCollect(static_cast<uint64_t>(timeout_seconds)));
+    if (callback != nullptr) {
+        auto status = callback->onFinish(result);
+        if (!status.isOk()) {
+            LOG(WARNING) << "Cannot return result " << toString(result)
+                         << " to callback: " << status.getDescription();
+        }
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+binder_status_t Storage::dump(int fd, const char**, uint32_t) {
+    DebugDump(fd);
+    return STATUS_OK;
+}
+
+}  // namespace aidl::android::hardware::health::storage
diff --git a/health/storage/aidl/default/Storage.h b/health/storage/aidl/default/Storage.h
new file mode 100644
index 0000000..049991b
--- /dev/null
+++ b/health/storage/aidl/default/Storage.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/health/storage/BnStorage.h>
+
+namespace aidl::android::hardware::health::storage {
+
+class Storage : public BnStorage {
+    ndk::ScopedAStatus garbageCollect(
+            int64_t timeout_seconds,
+            const std::shared_ptr<IGarbageCollectCallback>& callback) override;
+    binder_status_t dump(int fd, const char** args, uint32_t num_args) override;
+};
+
+}  // namespace aidl::android::hardware::health::storage
diff --git a/health/storage/aidl/default/health-storage-default.rc b/health/storage/aidl/default/health-storage-default.rc
new file mode 100644
index 0000000..fc1cc8b
--- /dev/null
+++ b/health/storage/aidl/default/health-storage-default.rc
@@ -0,0 +1,7 @@
+service vendor.health-storage-default /vendor/bin/hw/android.hardware.health.storage-service.default
+    interface aidl android.hardware.health.storage.IStorage/default
+    oneshot
+    disabled
+    class hal
+    user system
+    group system
diff --git a/health/storage/aidl/default/health-storage-default.xml b/health/storage/aidl/default/health-storage-default.xml
new file mode 100644
index 0000000..14d4901
--- /dev/null
+++ b/health/storage/aidl/default/health-storage-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.health.storage</name>
+        <version>1</version>
+        <fqname>IStorage/default</fqname>
+    </hal>
+</manifest>
diff --git a/health/storage/aidl/default/main.cpp b/health/storage/aidl/default/main.cpp
new file mode 100644
index 0000000..186b64c
--- /dev/null
+++ b/health/storage/aidl/default/main.cpp
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Storage.h"
+
+using aidl::android::hardware::health::storage::Storage;
+using std::string_literals::operator""s;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    // make a default storage service
+    auto storage = ndk::SharedRefBase::make<Storage>();
+    const std::string name = Storage::descriptor + "/default"s;
+    CHECK_EQ(STATUS_OK,
+             AServiceManager_registerLazyService(storage->asBinder().get(), name.c_str()));
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/health/storage/aidl/vts/functional/Android.bp b/health/storage/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..be3eac7
--- /dev/null
+++ b/health/storage/aidl/vts/functional/Android.bp
@@ -0,0 +1,46 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalHealthStorageTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "VtsHalHealthStorageTargetTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "android.hardware.health.storage-V1-ndk_platform",
+    ],
+    header_libs: [
+        "libhealth_storage_test_common_headers",
+    ],
+    test_suites: [
+        "vts",
+    ],
+    test_config: "VtsHalHealthStorageTargetTest.xml",
+}
diff --git a/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp
new file mode 100644
index 0000000..3b6b6b4
--- /dev/null
+++ b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.cpp
@@ -0,0 +1,136 @@
+/*
+ * 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.
+ */
+
+#include <unistd.h>
+
+#include <chrono>
+#include <set>
+#include <string>
+#include <thread>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/health/storage/BnGarbageCollectCallback.h>
+#include <aidl/android/hardware/health/storage/IStorage.h>
+#include <android-base/logging.h>
+#include <android/binder_ibinder.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include <health-storage-test/common.h>
+
+namespace aidl::android::hardware::health::storage {
+
+using namespace ::android::hardware::health::storage::test;
+using std::chrono_literals::operator""ms;
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk()) << ret.getDescription()
+#define EXPECT_OK(ret) EXPECT_TRUE(ret.isOk()) << ret.getDescription()
+
+class GcCallback : public BnGarbageCollectCallback, public Flag {
+  public:
+    ndk::ScopedAStatus onFinish(Result result) override {
+        std::unique_lock<std::mutex> lock(mutex_);
+        result_ = result;
+        OnFinishLocked(&lock);
+        return ndk::ScopedAStatus::ok();
+    }
+
+    /**
+     * Wait for a specific "timeout". If GC has finished, test that the result
+     * is equal to the "expected" value.
+     */
+    template <typename R, typename P>
+    void WaitForResult(std::chrono::duration<R, P> timeout, Result expected) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        ASSERT_TRUE(WaitLocked(&lock, timeout)) << "timeout after " << to_string(timeout);
+        EXPECT_EQ(expected, result_);
+    }
+
+  private:
+    Result result_{Result::UNKNOWN_ERROR};
+};
+
+class HealthStorageAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        std::string name = GetParam();
+        ASSERT_TRUE(AServiceManager_isDeclared(name.c_str())) << name;
+        ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
+        ASSERT_NE(binder, nullptr);
+        storage_ = IStorage::fromBinder(binder);
+        ASSERT_NE(storage_, nullptr);
+    }
+
+    virtual void TearDown() override {
+        EXPECT_TRUE(ping(kRpcTime))
+                << "Service is not responsive; expect subsequent tests to fail.";
+    }
+
+    /**
+     * Ping the service and expect it to return after "timeout". Return true
+     * iff the service is responsive within "timeout".
+     */
+    template <typename R, typename P>
+    bool ping(std::chrono::duration<R, P> timeout) {
+        // Ensure the service is responsive after the test.
+        std::shared_ptr<IStorage> service = storage_;
+        auto ping_flag = std::make_shared<Flag>();
+        std::thread([service, ping_flag] {
+            EXPECT_EQ(STATUS_OK, AIBinder_ping(service->asBinder().get()));
+            ping_flag->OnFinish();
+        }).detach();
+        return ping_flag->Wait(timeout);
+    }
+
+    std::shared_ptr<IStorage> storage_;
+};
+
+/**
+ * Ensure garbage collection works on null callback.
+ */
+TEST_P(HealthStorageAidl, GcNullCallback) {
+    ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, nullptr));
+
+    // Hold test process because HAL can be single-threaded and doing GC.
+    ASSERT_TRUE(ping(kDevGcTimeout + kDevGcTolerance + kRpcTime))
+            << "Service must be available after "
+            << to_string(kDevGcTimeout + kDevGcTolerance + kRpcTime);
+}
+
+/**
+ * Ensure garbage collection works on non-null callback.
+ */
+TEST_P(HealthStorageAidl, GcNonNullCallback) {
+    std::shared_ptr<GcCallback> cb = ndk::SharedRefBase::make<GcCallback>();
+    ASSERT_OK(storage_->garbageCollect(kDevGcTimeoutSec, cb));
+    cb->WaitForResult(kDevGcTimeout + kDevGcTolerance + kRpcTime, Result::SUCCESS);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HealthStorageAidl);
+INSTANTIATE_TEST_SUITE_P(
+        HealthStorage, HealthStorageAidl,
+        testing::ValuesIn(::android::getAidlHalInstanceNames(IStorage::descriptor)),
+        ::android::PrintInstanceNameToString);
+
+}  // namespace aidl::android::hardware::health::storage
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml
new file mode 100644
index 0000000..f8a1c87
--- /dev/null
+++ b/health/storage/aidl/vts/functional/VtsHalHealthStorageTargetTest.xml
@@ -0,0 +1,33 @@
+<?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 VtsHalHealthStorageTargetTest.">
+    <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.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalHealthStorageTargetTest->/data/local/tmp/VtsHalHealthStorageTargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalHealthStorageTargetTest" />
+        <option name="native-test-timeout" value="3m" />
+    </test>
+</configuration>
diff --git a/health/storage/impl_common/Android.bp b/health/storage/impl_common/Android.bp
new file mode 100644
index 0000000..925cd55
--- /dev/null
+++ b/health/storage/impl_common/Android.bp
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+// Common implementation between HIDL and AIDL HAL.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "libhealth_storage_impl_common",
+    vendor: true,
+    srcs: [
+        "impl_common.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+
+    cflags: [
+        "-Wall",
+        "-Werror",
+    ],
+
+    shared_libs: [
+        "libbase",
+        "libhidlbase",
+        "liblog",
+        "android.hardware.health.storage@1.0",
+    ],
+
+    static_libs: [
+        "libfstab",
+    ],
+
+    export_shared_lib_headers: [
+        "android.hardware.health.storage@1.0",
+    ],
+}
diff --git a/health/storage/impl_common/impl_common.cpp b/health/storage/impl_common/impl_common.cpp
new file mode 100644
index 0000000..1a7a7f1
--- /dev/null
+++ b/health/storage/impl_common/impl_common.cpp
@@ -0,0 +1,154 @@
+/*
+ * 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.
+ */
+#include <health-storage-impl/common.h>
+
+#include <android-base/chrono_utils.h>
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <fstab/fstab.h>
+
+using ::android::base::ReadFileToString;
+using ::android::base::Timer;
+using ::android::base::Trim;
+using ::android::base::WriteStringToFd;
+using ::android::base::WriteStringToFile;
+using ::android::fs_mgr::Fstab;
+using ::android::fs_mgr::ReadDefaultFstab;
+using ::android::hardware::health::storage::V1_0::Result;
+
+namespace android::hardware::health::storage {
+
+static std::string GetGarbageCollectPath() {
+    Fstab fstab;
+    ReadDefaultFstab(&fstab);
+
+    for (const auto& entry : fstab) {
+        if (!entry.sysfs_path.empty()) {
+            return entry.sysfs_path + "/manual_gc";
+        }
+    }
+
+    return "";
+}
+
+static std::string GetWriteBoosterPath() {
+    Fstab fstab;
+    ReadDefaultFstab(&fstab);
+
+    for (const auto& entry : fstab) {
+        if (!entry.sysfs_path.empty()) {
+            return entry.sysfs_path + "/attributes/wb_avail_buf";
+        }
+    }
+
+    return "";
+}
+
+Result GarbageCollect(uint64_t timeout_seconds) {
+    std::string gc_path = GetGarbageCollectPath();
+
+    if (gc_path.empty()) {
+        LOG(WARNING) << "Cannot find Dev GC path";
+        return Result::UNKNOWN_ERROR;
+    }
+
+    Result result = Result::SUCCESS;
+    Timer timer;
+    LOG(INFO) << "Start Dev GC on " << gc_path;
+    while (1) {
+        std::string require_gc;
+        if (!ReadFileToString(gc_path, &require_gc)) {
+            PLOG(WARNING) << "Reading manual_gc failed in " << gc_path;
+            result = Result::IO_ERROR;
+            break;
+        }
+        require_gc = Trim(require_gc);
+
+        std::string wb_path = GetWriteBoosterPath();
+        // Let's flush WB till 100% available
+        std::string wb_avail = "0x0000000A";
+        if (!wb_path.empty() && !ReadFileToString(wb_path, &wb_avail)) {
+            PLOG(WARNING) << "Reading wb_avail_buf failed in " << wb_path;
+        }
+        wb_avail = Trim(wb_avail);
+
+        if (require_gc == "disabled") {
+            LOG(DEBUG) << "Disabled Dev GC";
+            break;
+        }
+        if ((require_gc == "" || require_gc == "off") && wb_avail == "0x0000000A") {
+            LOG(DEBUG) << "No more to do Dev GC";
+            break;
+        }
+        LOG(DEBUG) << "Trigger Dev GC on " << gc_path << " having " << require_gc << ", WB on "
+                   << wb_path << " having " << wb_avail;
+        if (!WriteStringToFile("1", gc_path)) {
+            PLOG(WARNING) << "Start Dev GC failed on " << gc_path;
+            result = Result::IO_ERROR;
+            break;
+        }
+        if (timer.duration() >= std::chrono::seconds(timeout_seconds)) {
+            LOG(WARNING) << "Dev GC timeout";
+            // Timeout is not treated as an error. Try next time.
+            break;
+        }
+        sleep(2);
+    }
+    LOG(INFO) << "Stop Dev GC on " << gc_path;
+    if (!WriteStringToFile("0", gc_path)) {
+        PLOG(WARNING) << "Stop Dev GC failed on " << gc_path;
+        result = Result::IO_ERROR;
+    }
+
+    return result;
+}
+
+void DebugDump(int fd) {
+    std::stringstream output;
+
+    std::string path = GetGarbageCollectPath();
+    if (path.empty()) {
+        output << "Cannot find Dev GC path";
+    } else {
+        std::string require_gc;
+
+        if (ReadFileToString(path, &require_gc)) {
+            output << path << ":" << require_gc << std::endl;
+        }
+
+        if (WriteStringToFile("0", path)) {
+            output << "stop success" << std::endl;
+        }
+    }
+    std::string wb_path = GetWriteBoosterPath();
+    if (wb_path.empty()) {
+        output << "Cannot find Dev WriteBooster path";
+    } else {
+        std::string wb_available;
+
+        if (ReadFileToString(wb_path, &wb_available)) {
+            output << wb_path << ":" << wb_available << std::endl;
+        }
+    }
+    if (!WriteStringToFd(output.str(), fd)) {
+        PLOG(WARNING) << "debug: cannot write to fd";
+    }
+
+    fsync(fd);
+}
+
+}  // namespace android::hardware::health::storage
diff --git a/health/storage/impl_common/include/health-storage-impl/common.h b/health/storage/impl_common/include/health-storage-impl/common.h
new file mode 100644
index 0000000..c84a6a9
--- /dev/null
+++ b/health/storage/impl_common/include/health-storage-impl/common.h
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android/hardware/health/storage/1.0/types.h>
+#include <string>
+
+namespace android::hardware::health::storage {
+
+// Run debug on fd
+void DebugDump(int fd);
+
+// Run garbage collection on GetGarbageCollectPath(). Blocks until garbage
+// collect finishes or |timeout_seconds| has reached.
+V1_0::Result GarbageCollect(uint64_t timeout_seconds);
+
+}  // namespace android::hardware::health::storage
diff --git a/health/storage/test_common/Android.bp b/health/storage/test_common/Android.bp
new file mode 100644
index 0000000..a43aae5
--- /dev/null
+++ b/health/storage/test_common/Android.bp
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_headers {
+    name: "libhealth_storage_test_common_headers",
+    export_include_dirs: ["include"],
+}
diff --git a/health/storage/test_common/include/health-storage-test/common.h b/health/storage/test_common/include/health-storage-test/common.h
new file mode 100644
index 0000000..dfda830
--- /dev/null
+++ b/health/storage/test_common/include/health-storage-test/common.h
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <chrono>
+#include <string>
+
+namespace android::hardware::health::storage::test {
+
+// Dev GC timeout. This is the timeout used by vold.
+const uint64_t kDevGcTimeoutSec = 120;
+const std::chrono::seconds kDevGcTimeout{kDevGcTimeoutSec};
+// Dev GC timeout tolerance. The HAL may not immediately return after the
+// timeout, so include an acceptable tolerance.
+const std::chrono::seconds kDevGcTolerance{3};
+// Time accounted for RPC calls.
+const std::chrono::milliseconds kRpcTime{1000};
+
+template <typename R>
+std::string to_string(std::chrono::duration<R, std::milli> time) {
+    return std::to_string(time.count()) + "ms";
+}
+
+/** An atomic boolean flag that indicates whether a task has finished. */
+class Flag {
+  public:
+    void OnFinish() {
+        std::unique_lock<std::mutex> lock(mutex_);
+        OnFinishLocked(&lock);
+    }
+    template <typename R, typename P>
+    bool Wait(std::chrono::duration<R, P> duration) {
+        std::unique_lock<std::mutex> lock(mutex_);
+        return WaitLocked(&lock, duration);
+    }
+
+  protected:
+    /** Will unlock. */
+    void OnFinishLocked(std::unique_lock<std::mutex>* lock) {
+        finished_ = true;
+        lock->unlock();
+        cv_.notify_all();
+    }
+    template <typename R, typename P>
+    bool WaitLocked(std::unique_lock<std::mutex>* lock, std::chrono::duration<R, P> duration) {
+        cv_.wait_for(*lock, duration, [this] { return finished_; });
+        return finished_;
+    }
+
+    bool finished_{false};
+    std::mutex mutex_;
+    std::condition_variable cv_;
+};
+
+}  // namespace android::hardware::health::storage::test
diff --git a/health/utils/libhealth2impl/Android.bp b/health/utils/libhealth2impl/Android.bp
index 14374a2..58417af 100644
--- a/health/utils/libhealth2impl/Android.bp
+++ b/health/utils/libhealth2impl/Android.bp
@@ -14,6 +14,15 @@
 
 // A helper library for health@2.x HAL implementation.
 // HAL implementations can link to this library and extend the Health class.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libhealth2impl",
     vendor_available: true,
diff --git a/health/utils/libhealth2impl/Health.cpp b/health/utils/libhealth2impl/Health.cpp
index f4684ae..035b36f 100644
--- a/health/utils/libhealth2impl/Health.cpp
+++ b/health/utils/libhealth2impl/Health.cpp
@@ -80,14 +80,14 @@
 
 Return<Result> Health::update() {
     Result result = Result::UNKNOWN;
-    getHealthInfo_2_1([&](auto res, const auto& /* health_info */) {
+    getHealthInfo_2_1([&](auto res, const auto& health_info) {
         result = res;
         if (res != Result::SUCCESS) {
             LOG(ERROR) << "Cannot call getHealthInfo_2_1: " << toString(res);
             return;
         }
 
-        battery_monitor_.logValues();
+        BatteryMonitor::logValues(health_info, *healthd_config_);
     });
     return result;
 }
diff --git a/health/utils/libhealthloop/Android.bp b/health/utils/libhealthloop/Android.bp
index de0f24f..7aaf905 100644
--- a/health/utils/libhealthloop/Android.bp
+++ b/health/utils/libhealthloop/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libhealthloop",
     vendor_available: true,
diff --git a/identity/TEST_MAPPING b/identity/TEST_MAPPING
new file mode 100644
index 0000000..85cf91f
--- /dev/null
+++ b/identity/TEST_MAPPING
@@ -0,0 +1,16 @@
+{
+  "presubmit": [
+    {
+      "name": "CtsIdentityTestCases"
+    },
+    {
+      "name": "VtsHalIdentityTargetTest"
+    },
+    {
+      "name": "android.hardware.identity-support-lib-test"
+    },
+    {
+      "name": "libeic_test"
+    }
+  ]
+}
diff --git a/identity/aidl/Android.bp b/identity/aidl/Android.bp
index 14aef8e..8da6642 100644
--- a/identity/aidl/Android.bp
+++ b/identity/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.identity",
     vendor_available: true,
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
index 7e3002d..d8a8128 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/Certificate.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
index 447203f..2685525 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/CipherSuite.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
index e1296e0..f8d5a9e 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/HardwareInformation.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
index 88104d9..3224e4b 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredential.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -18,6 +33,9 @@
 package android.hardware.identity;
 @VintfStability
 interface IIdentityCredential {
+  /**
+   * @deprecated use deleteCredentalWithChallenge() instead.
+   */
   byte[] deleteCredential();
   byte[] createEphemeralKeyPair();
   void setReaderEphemeralPublicKey(in byte[] publicKey);
@@ -25,8 +43,11 @@
   void startRetrieval(in android.hardware.identity.SecureAccessControlProfile[] accessControlProfiles, in android.hardware.keymaster.HardwareAuthToken authToken, in byte[] itemsRequest, in byte[] signingKeyBlob, in byte[] sessionTranscript, in byte[] readerSignature, in int[] requestCounts);
   void startRetrieveEntryValue(in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize, in int[] accessControlProfileIds);
   byte[] retrieveEntryValue(in byte[] encryptedContent);
-  void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
-  android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
+  @SuppressWarnings(value={"out-array"}) void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
+  @SuppressWarnings(value={"out-array"}) android.hardware.identity.Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
   void setRequestedNamespaces(in android.hardware.identity.RequestNamespace[] requestNamespaces);
   void setVerificationToken(in android.hardware.keymaster.VerificationToken verificationToken);
+  byte[] deleteCredentialWithChallenge(in byte[] challenge);
+  byte[] proveOwnership(in byte[] challenge);
+  android.hardware.identity.IWritableIdentityCredential updateCredential();
 }
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
index 5dafb76..c6fb3c8 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
index c5ac9d6..19a29ec 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
@@ -23,6 +38,6 @@
   android.hardware.identity.SecureAccessControlProfile addAccessControlProfile(in int id, in android.hardware.identity.Certificate readerCertificate, in boolean userAuthenticationRequired, in long timeoutMillis, in long secureUserId);
   void beginAddEntry(in int[] accessControlProfileIds, in @utf8InCpp String nameSpace, in @utf8InCpp String name, in int entrySize);
   byte[] addEntryValue(in byte[] content);
-  void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
+  @SuppressWarnings(value={"out-array"}) void finishAddingEntries(out byte[] credentialData, out byte[] proofOfProvisioningSignature);
   void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
 }
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl
index 24ec26a..c9c2b9f 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestDataItem.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl
index af00f3b..aaf1e20 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/RequestNamespace.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
index dfc1ad0..695fb3f 100644
--- a/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
+++ b/identity/aidl/aidl_api/android.hardware.identity/current/android/hardware/identity/SecureAccessControlProfile.aidl
@@ -1,14 +1,29 @@
-///////////////////////////////////////////////////////////////////////////////
+/*
+ * Copyright 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.
+ *////////////////////////////////////////////////////////////////////////////////
 // THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
 ///////////////////////////////////////////////////////////////////////////////
 
-// This file is a snapshot of an AIDL interface (or parcelable). Do not try to
-// edit this file. It looks like you are doing that because you have modified
-// an AIDL interface in a backward-incompatible way, e.g., deleting a function
-// from an interface or a field from a parcelable and it broke the build. That
-// breakage is intended.
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
 //
-// You must not make a backward incompatible changes to the AIDL files built
+// You must not make a backward incompatible change to any AIDL file built
 // with the aidl_interface module type with versions property set. The module
 // type is used to build AIDL files in a way that they can be used across
 // independently updatable components of the system. If a device is shipped
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
index 702334d..8ae293b 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredential.aidl
@@ -19,6 +19,7 @@
 import android.hardware.identity.Certificate;
 import android.hardware.identity.RequestNamespace;
 import android.hardware.identity.SecureAccessControlProfile;
+import android.hardware.identity.IWritableIdentityCredential;
 import android.hardware.keymaster.HardwareAuthToken;
 import android.hardware.keymaster.VerificationToken;
 
@@ -40,7 +41,11 @@
      * After this method has been called, the persistent storage used for credentialData should
      * be deleted.
      *
-     * @return a COSE_Sign1 signature described above.
+     * This method was deprecated in API version 3 because there's no challenge so freshness
+     * can't be checked. Use deleteCredentalWithChallenge() instead.
+     *
+     * @return a COSE_Sign1 signature described above
+     * @deprecated use deleteCredentalWithChallenge() instead.
      */
     byte[] deleteCredential();
 
@@ -319,6 +324,7 @@
      *
      * @param out deviceNameSpaces the bytes of DeviceNameSpaces.
      */
+    @SuppressWarnings(value={"out-array"})
     void finishRetrieval(out byte[] mac, out byte[] deviceNameSpaces);
 
     /**
@@ -353,12 +359,25 @@
      *
      *  - subjectPublicKeyInfo: must contain attested public key.
      *
+     * As of API version 3, the certificate shall also have an X.509 extension at
+     * OID 1.3.6.1.4.1.11129.2.1.26 which shall contain an OCTET STRING with the
+     * bytes of the CBOR with the following CDDL:
+     *
+     *   ProofOfBinding = [
+     *     "ProofOfBinding",
+     *     bstr,              // Contains SHA-256(ProofOfProvisioning)
+     *   ]
+     *
+     * This CBOR enables an issuer to determine the exact state of the credential it
+     * returns issuer-signed data for.
+     *
      * @param out signingKeyBlob contains an AES-GCM-ENC(storageKey, R, signingKey, docType)
      *     where signingKey is an EC private key in uncompressed form. That is, the returned
      *     blob is an encrypted copy of the newly-generated private signing key.
      *
      * @return an X.509 certificate for the new signing key, signed by the credential key.
      */
+    @SuppressWarnings(value={"out-array"})
     Certificate generateSigningKeyPair(out byte[] signingKeyBlob);
 
     /**
@@ -381,4 +400,63 @@
     *   The verification token. This token is only valid if the timestamp field is non-zero.
     */
     void setVerificationToken(in VerificationToken verificationToken);
+
+    /**
+     * Delete a credential.
+     *
+     * This method returns a COSE_Sign1 data structure signed by CredentialKey
+     * with payload set to the ProofOfDeletion CBOR below:
+     *
+     *     ProofOfDeletion = [
+     *          "ProofOfDeletion",            ; tstr
+     *          tstr,                         ; DocType
+     *          bstr,                         ; Challenge
+     *          bool                          ; true if this is a test credential, should
+     *                                        ; always be false.
+     *     ]
+     *
+     * After this method has been called, the persistent storage used for credentialData should
+     * be deleted.
+     *
+     * This method was introduced in API version 3.
+     *
+     * @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
+     *     and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
+     * @return a COSE_Sign1 signature described above.
+     */
+    byte[] deleteCredentialWithChallenge(in byte[] challenge);
+
+    /**
+     * Prove ownership of credential.
+     *
+     * This method returns a COSE_Sign1 data structure signed by CredentialKey with payload
+     * set to the ProofOfOwnership CBOR below.
+     *
+     *     ProofOfOwnership = [
+     *          "ProofOfOwnership",           ; tstr
+     *          tstr,                         ; DocType
+     *          bstr,                         ; Challenge
+     *          bool                          ; true if this is a test credential, should
+     *                                        ; always be false.
+     *     ]
+     *
+     * This method was introduced in API version 3.
+     *
+     * @param challenge a challenge set by the issuer to ensure freshness. Maximum size is 32 bytes
+     *     and it may be empty. Fails with STATUS_INVALID_DATA if bigger than 32 bytes.
+     * @return a COSE_Sign1 signature described above.
+     */
+    byte[] proveOwnership(in byte[] challenge);
+
+    /**
+     * Called to start updating the credential with new data items.
+     *
+     * If the getAttestationCertificate() method is called on the returned object
+     * it fails with the error STATUS_FAILED.
+     *
+     * This method was introduced in API version 3.
+     *
+     * @return an IWritableIdentityCredential
+     */
+    IWritableIdentityCredential updateCredential();
 }
diff --git a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
index 33e25b1..638be79 100644
--- a/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
+++ b/identity/aidl/android/hardware/identity/IIdentityCredentialStore.aidl
@@ -104,6 +104,11 @@
  * All binder calls in the HAL may return a ServiceSpecificException with statuses from the
  * STATUS_* integers defined in this interface. Each method states which status can be returned
  * and under which circumstances.
+ *
+ * The API described here is API version 3 which corresponds to feature version 202101
+ * of the android.security.identity Framework API. An XML file declaring the feature
+ * android.hardware.identity_credential (or android.hardware.identity_credential.direct_access
+ * if implementing the Direct Access HAL) should be included declaring this feature version.
  */
 @VintfStability
 interface IIdentityCredentialStore {
@@ -230,6 +235,9 @@
      *     return argument of the same name in finishAddingEntries(), in
      *     IWritableIdentityCredential.
      *
+     *     Note that the format of credentialData may depend on the feature version.
+     *     Implementations must support credentialData created by an earlier feature version.
+     *
      * @return an IIdentityCredential interface that provides operations on the Credential.
      */
     IIdentityCredential getCredential(in CipherSuite cipherSuite, in byte[] credentialData);
diff --git a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
index c48cb66..22bcf61 100644
--- a/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
+++ b/identity/aidl/android/hardware/identity/IWritableIdentityCredential.aidl
@@ -263,7 +263,9 @@
      *
      *     where HBK is a unique hardware-bound key that has never existed outside of the secure
      *     environment (except it's all zeroes if testCredential is True) and CredentialKeys is
-     *     the CBOR-encoded structure (in CDDL notation):
+     *     the CBOR-encoded structure (in CDDL notation) given below.
+     *
+     *     In API versions 1 and 2 it was the following
      *
      *         CredentialKeys = [
      *              bstr,   ; storageKey, a 128-bit AES key
@@ -271,6 +273,17 @@
      *                      ; in uncompressed form
      *         ]
      *
+     *     In API version 3 or later it must be the following
+     *
+     *         CredentialKeys = [
+     *              bstr,   ; storageKey, a 128-bit AES key
+     *              bstr    ; credentialPrivKey, the private key for credentialKey
+     *                      ; in uncompressed form
+     *              bstr    ; SHA-256(ProofOfProvisioning)
+     *         ]
+     *
+     *     Additional elements may be added to the CredentialKeys array in future versions.
+     *
      * @param out proofOfProvisioningSignature proves to the IA that the credential was imported
      *     into the secure hardware without alteration or error.  When the final addEntry() call is
      *     made (when the number of provisioned entries equals the sum of the items in
@@ -307,6 +320,7 @@
      *              "accessControlProfiles" : [ * uint ],
      *          }
      */
+    @SuppressWarnings(value={"out-array"})
     void finishAddingEntries(out byte[] credentialData,
         out byte[] proofOfProvisioningSignature);
 
@@ -321,4 +335,5 @@
      * @param expectedProofOfProvisioningSize the expected size of ProofOfProvisioning.
      */
     void setExpectedProofOfProvisioningSize(in int expectedProofOfProvisioningSize);
+
 }
diff --git a/identity/aidl/default/Android.bp b/identity/aidl/default/Android.bp
index 2eb0faa..28c4893 100644
--- a/identity/aidl/default/Android.bp
+++ b/identity/aidl/default/Android.bp
@@ -1,3 +1,78 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "android.hardware.identity-libeic-hal-common",
+    vendor_available: true,
+    srcs: [
+        "common/IdentityCredential.cpp",
+        "common/IdentityCredentialStore.cpp",
+        "common/WritableIdentityCredential.cpp",
+    ],
+    export_include_dirs: [
+        "common",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Wno-deprecated-declarations",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcrypto",
+        "libbinder_ndk",
+        "libkeymaster_messages",
+    ],
+    static_libs: [
+        "libbase",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libutils",
+        "libsoft_attestation_cert",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
+        "android.hardware.identity-support-lib",
+        "android.hardware.identity-V3-ndk_platform",
+        "android.hardware.keymaster-V3-ndk_platform",
+    ],
+}
+
+cc_library_static {
+    name: "android.hardware.identity-libeic-library",
+    vendor_available: true,
+    srcs: [
+        "libeic/EicCbor.c",
+        "libeic/EicPresentation.c",
+        "libeic/EicProvisioning.c",
+        "EicOpsImpl.cc",
+    ],
+    export_include_dirs: [
+        "libeic",
+    ],
+    cflags: [
+        "-DEIC_COMPILATION",
+        "-Wall",
+        "-Wextra",
+        "-DEIC_DEBUG",
+        // Allow using C2x extensions such as omitting parameter names
+        "-Wno-c2x-extensions",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcrypto",
+    ],
+    static_libs: [
+        "android.hardware.identity-support-lib",
+    ],
+}
+
 cc_binary {
     name: "android.hardware.identity-service.example",
     relative_install_path: "hw",
@@ -7,23 +82,78 @@
     cflags: [
         "-Wall",
         "-Wextra",
+        "-g",
     ],
     shared_libs: [
-        "libbase",
-        "libbinder_ndk",
-        "libcppbor",
-        "libcrypto",
         "liblog",
+        "libcrypto",
+        "libbinder_ndk",
+        "libkeymaster_messages",
+    ],
+    static_libs: [
+        "libbase",
+        "libcppbor_external",
+        "libcppcose_rkp",
         "libutils",
+        "libsoft_attestation_cert",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
         "android.hardware.identity-support-lib",
-        "android.hardware.identity-ndk_platform",
-        "android.hardware.keymaster-ndk_platform",
+        "android.hardware.identity-V3-ndk_platform",
+        "android.hardware.keymaster-V3-ndk_platform",
+        "android.hardware.identity-libeic-hal-common",
+        "android.hardware.identity-libeic-library",
     ],
     srcs: [
-        "IdentityCredential.cpp",
-        "IdentityCredentialStore.cpp",
-        "WritableIdentityCredential.cpp",
-        "Util.cpp",
         "service.cpp",
+        "FakeSecureHardwareProxy.cpp",
     ],
+    required: [
+        "android.hardware.identity_credential.xml",
+    ],
+}
+
+cc_test {
+    name: "libeic_test",
+    srcs: [
+        "EicTests.cpp",
+        "FakeSecureHardwareProxy.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-g",
+        "-DEIC_DEBUG",
+    ],
+    local_include_dirs: [
+         "common",
+    ],
+    shared_libs: [
+        "liblog",
+        "libcrypto",
+        "libkeymaster_messages",
+    ],
+    static_libs: [
+        "libbase",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libutils",
+        "libsoft_attestation_cert",
+        "libkeymaster_portable",
+        "libsoft_attestation_cert",
+        "libpuresoftkeymasterdevice",
+        "android.hardware.identity-support-lib",
+        "android.hardware.identity-libeic-library",
+    ],
+    test_suites: [
+        "general-tests",
+    ],
+}
+
+prebuilt_etc {
+    name: "android.hardware.identity_credential.xml",
+    sub_dir: "permissions",
+    vendor: true,
+    src: "android.hardware.identity_credential.xml",
 }
diff --git a/identity/aidl/default/EicOpsImpl.cc b/identity/aidl/default/EicOpsImpl.cc
new file mode 100644
index 0000000..8ec4cc9
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.cc
@@ -0,0 +1,515 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "EicOpsImpl"
+
+#include <optional>
+#include <tuple>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include "EicOps.h"
+
+using ::std::map;
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+void* eicMemSet(void* s, int c, size_t n) {
+    return memset(s, c, n);
+}
+
+void* eicMemCpy(void* dest, const void* src, size_t n) {
+    return memcpy(dest, src, n);
+}
+
+size_t eicStrLen(const char* s) {
+    return strlen(s);
+}
+
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n) {
+    return CRYPTO_memcmp(s1, s2, n);
+}
+
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    HMAC_CTX_init(realCtx);
+    if (HMAC_Init_ex(realCtx, key, keySize, EVP_sha256(), nullptr /* impl */) != 1) {
+        LOG(ERROR) << "Error initializing HMAC_CTX";
+    }
+}
+
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    if (HMAC_Update(realCtx, data, len) != 1) {
+        LOG(ERROR) << "Error updating HMAC_CTX";
+    }
+}
+
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    HMAC_CTX* realCtx = (HMAC_CTX*)ctx;
+    unsigned int size = 0;
+    if (HMAC_Final(realCtx, digest, &size) != 1) {
+        LOG(ERROR) << "Error finalizing HMAC_CTX";
+    }
+    if (size != EIC_SHA256_DIGEST_SIZE) {
+        LOG(ERROR) << "Expected 32 bytes from HMAC_Final, got " << size;
+    }
+}
+
+void eicOpsSha256Init(EicSha256Ctx* ctx) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Init(realCtx);
+}
+
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Update(realCtx, data, len);
+}
+
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    SHA256_CTX* realCtx = (SHA256_CTX*)ctx;
+    SHA256_Final(digest, realCtx);
+}
+
+bool eicOpsRandom(uint8_t* buf, size_t numBytes) {
+    optional<vector<uint8_t>> bytes = ::android::hardware::identity::support::getRandom(numBytes);
+    if (!bytes.has_value()) {
+        return false;
+    }
+    memcpy(buf, bytes.value().data(), numBytes);
+    return true;
+}
+
+bool eicOpsEncryptAes128Gcm(
+        const uint8_t* key,    // Must be 16 bytes
+        const uint8_t* nonce,  // Must be 12 bytes
+        const uint8_t* data,   // May be NULL if size is 0
+        size_t dataSize,
+        const uint8_t* additionalAuthenticationData,  // May be NULL if size is 0
+        size_t additionalAuthenticationDataSize, uint8_t* encryptedData) {
+    vector<uint8_t> cppKey;
+    cppKey.resize(16);
+    memcpy(cppKey.data(), key, 16);
+
+    vector<uint8_t> cppData;
+    cppData.resize(dataSize);
+    if (dataSize > 0) {
+        memcpy(cppData.data(), data, dataSize);
+    }
+
+    vector<uint8_t> cppAAD;
+    cppAAD.resize(additionalAuthenticationDataSize);
+    if (additionalAuthenticationDataSize > 0) {
+        memcpy(cppAAD.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+    }
+
+    vector<uint8_t> cppNonce;
+    cppNonce.resize(12);
+    memcpy(cppNonce.data(), nonce, 12);
+
+    optional<vector<uint8_t>> cppEncryptedData =
+            android::hardware::identity::support::encryptAes128Gcm(cppKey, cppNonce, cppData,
+                                                                   cppAAD);
+    if (!cppEncryptedData.has_value()) {
+        return false;
+    }
+
+    memcpy(encryptedData, cppEncryptedData.value().data(), cppEncryptedData.value().size());
+    return true;
+}
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key,  // Must be 16 bytes
+                            const uint8_t* encryptedData, size_t encryptedDataSize,
+                            const uint8_t* additionalAuthenticationData,
+                            size_t additionalAuthenticationDataSize, uint8_t* data) {
+    vector<uint8_t> keyVec;
+    keyVec.resize(16);
+    memcpy(keyVec.data(), key, 16);
+
+    vector<uint8_t> encryptedDataVec;
+    encryptedDataVec.resize(encryptedDataSize);
+    if (encryptedDataSize > 0) {
+        memcpy(encryptedDataVec.data(), encryptedData, encryptedDataSize);
+    }
+
+    vector<uint8_t> aadVec;
+    aadVec.resize(additionalAuthenticationDataSize);
+    if (additionalAuthenticationDataSize > 0) {
+        memcpy(aadVec.data(), additionalAuthenticationData, additionalAuthenticationDataSize);
+    }
+
+    optional<vector<uint8_t>> decryptedDataVec =
+            android::hardware::identity::support::decryptAes128Gcm(keyVec, encryptedDataVec,
+                                                                   aadVec);
+    if (!decryptedDataVec.has_value()) {
+        eicDebug("Error decrypting data");
+        return false;
+    }
+    if (decryptedDataVec.value().size() != encryptedDataSize - 28) {
+        eicDebug("Decrypted data is size %zd, expected %zd", decryptedDataVec.value().size(),
+                 encryptedDataSize - 28);
+        return false;
+    }
+
+    if (decryptedDataVec.value().size() > 0) {
+        memcpy(data, decryptedDataVec.value().data(), decryptedDataVec.value().size());
+    }
+    return true;
+}
+
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                       uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]) {
+    optional<vector<uint8_t>> keyPair = android::hardware::identity::support::createEcKeyPair();
+    if (!keyPair) {
+        eicDebug("Error creating EC keypair");
+        return false;
+    }
+    optional<vector<uint8_t>> privKey =
+            android::hardware::identity::support::ecKeyPairGetPrivateKey(keyPair.value());
+    if (!privKey) {
+        eicDebug("Error extracting private key");
+        return false;
+    }
+    if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+        eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+                 (size_t)EIC_P256_PRIV_KEY_SIZE);
+        return false;
+    }
+
+    optional<vector<uint8_t>> pubKey =
+            android::hardware::identity::support::ecKeyPairGetPublicKey(keyPair.value());
+    if (!pubKey) {
+        eicDebug("Error extracting public key");
+        return false;
+    }
+    // ecKeyPairGetPublicKey() returns 0x04 | x | y, we don't want the leading 0x04.
+    if (pubKey.value().size() != EIC_P256_PUB_KEY_SIZE + 1) {
+        eicDebug("Public key is %zd bytes long, expected %zd", pubKey.value().size(),
+                 (size_t)EIC_P256_PRIV_KEY_SIZE + 1);
+        return false;
+    }
+
+    memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+    memcpy(publicKey, pubKey.value().data() + 1, EIC_P256_PUB_KEY_SIZE);
+
+    return true;
+}
+
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+                               size_t challengeSize, const uint8_t* applicationId,
+                               size_t applicationIdSize, bool testCredential, uint8_t* cert,
+                               size_t* certSize) {
+    vector<uint8_t> challengeVec(challengeSize);
+    memcpy(challengeVec.data(), challenge, challengeSize);
+
+    vector<uint8_t> applicationIdVec(applicationIdSize);
+    memcpy(applicationIdVec.data(), applicationId, applicationIdSize);
+
+    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> ret =
+            android::hardware::identity::support::createEcKeyPairAndAttestation(
+                    challengeVec, applicationIdVec, testCredential);
+    if (!ret) {
+        eicDebug("Error generating CredentialKey and attestation");
+        return false;
+    }
+
+    // Extract certificate chain.
+    vector<uint8_t> flatChain =
+            android::hardware::identity::support::certificateChainJoin(ret.value().second);
+    if (*certSize < flatChain.size()) {
+        eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+                 flatChain.size());
+        return false;
+    }
+    memcpy(cert, flatChain.data(), flatChain.size());
+    *certSize = flatChain.size();
+
+    // Extract private key.
+    optional<vector<uint8_t>> privKey =
+            android::hardware::identity::support::ecKeyPairGetPrivateKey(ret.value().first);
+    if (!privKey) {
+        eicDebug("Error extracting private key");
+        return false;
+    }
+    if (privKey.value().size() != EIC_P256_PRIV_KEY_SIZE) {
+        eicDebug("Private key is %zd bytes, expected %zd", privKey.value().size(),
+                 (size_t)EIC_P256_PRIV_KEY_SIZE);
+        return false;
+    }
+
+    memcpy(privateKey, privKey.value().data(), EIC_P256_PRIV_KEY_SIZE);
+
+    return true;
+}
+
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                     const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+                     const char* issuerName, const char* subjectName, time_t validityNotBefore,
+                     time_t validityNotAfter, const uint8_t* proofOfBinding,
+                     size_t proofOfBindingSize, uint8_t* cert, size_t* certSize) {  // inout
+    vector<uint8_t> signingKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(signingKeyVec.data(), signingKey, EIC_P256_PRIV_KEY_SIZE);
+
+    vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+    pubKeyVec[0] = 0x04;
+    memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+    string serialDecimal = android::base::StringPrintf("%d", serial);
+
+    map<string, vector<uint8_t>> extensions;
+    if (proofOfBinding != nullptr) {
+        vector<uint8_t> proofOfBindingVec(proofOfBinding, proofOfBinding + proofOfBindingSize);
+        extensions["1.3.6.1.4.1.11129.2.1.26"] = proofOfBindingVec;
+    }
+
+    optional<vector<uint8_t>> certVec =
+            android::hardware::identity::support::ecPublicKeyGenerateCertificate(
+                    pubKeyVec, signingKeyVec, serialDecimal, issuerName, subjectName,
+                    validityNotBefore, validityNotAfter, extensions);
+    if (!certVec) {
+        eicDebug("Error generating certificate");
+        return false;
+    }
+
+    if (*certSize < certVec.value().size()) {
+        eicDebug("Buffer for certificate is only %zd bytes long, need %zd bytes", *certSize,
+                 certVec.value().size());
+        return false;
+    }
+    memcpy(cert, certVec.value().data(), certVec.value().size());
+    *certSize = certVec.value().size();
+
+    return true;
+}
+
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                 const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+                 uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+    vector<uint8_t> digestVec(EIC_SHA256_DIGEST_SIZE);
+    memcpy(digestVec.data(), digestOfData, EIC_SHA256_DIGEST_SIZE);
+
+    optional<vector<uint8_t>> derSignature =
+            android::hardware::identity::support::signEcDsaDigest(privKeyVec, digestVec);
+    if (!derSignature) {
+        eicDebug("Error signing data");
+        return false;
+    }
+
+    ECDSA_SIG* sig;
+    const unsigned char* p = derSignature.value().data();
+    sig = d2i_ECDSA_SIG(nullptr, &p, derSignature.value().size());
+    if (sig == nullptr) {
+        eicDebug("Error decoding DER signature");
+        return false;
+    }
+
+    if (BN_bn2binpad(sig->r, signature, 32) != 32) {
+        eicDebug("Error encoding r");
+        return false;
+    }
+    if (BN_bn2binpad(sig->s, signature + 32, 32) != 32) {
+        eicDebug("Error encoding s");
+        return false;
+    }
+
+    return true;
+}
+
+static const uint8_t hbkTest[16] = {0};
+static const uint8_t hbkReal[16] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
+
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential) {
+    if (testCredential) {
+        return hbkTest;
+    }
+    return hbkReal;
+}
+
+bool eicOpsValidateAuthToken(uint64_t /* challenge */, uint64_t /* secureUserId */,
+                             uint64_t /* authenticatorId */, int /* hardwareAuthenticatorType */,
+                             uint64_t /* timeStamp */, const uint8_t* /* mac */,
+                             size_t /* macSize */, uint64_t /* verificationTokenChallenge */,
+                             uint64_t /* verificationTokenTimeStamp */,
+                             int /* verificationTokenSecurityLevel */,
+                             const uint8_t* /* verificationTokenMac */,
+                             size_t /* verificationTokenMacSize */) {
+    // Here's where we would validate the passed-in |authToken| to assure ourselves
+    // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
+    //
+    // However this involves calculating the MAC which requires access to the to
+    // a pre-shared key which we don't have...
+    //
+    return true;
+}
+
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+                            size_t* publicKeySize) {
+    vector<uint8_t> chain;
+    chain.resize(x509CertSize);
+    memcpy(chain.data(), x509Cert, x509CertSize);
+    optional<vector<uint8_t>> res =
+            android::hardware::identity::support::certificateChainGetTopMostKey(chain);
+    if (!res) {
+        return false;
+    }
+    if (res.value().size() > *publicKeySize) {
+        eicDebug("Public key size is %zd but buffer only has room for %zd bytes",
+                 res.value().size(), *publicKeySize);
+        return false;
+    }
+    *publicKeySize = res.value().size();
+    memcpy(publicKey, res.value().data(), *publicKeySize);
+    eicDebug("Extracted %zd bytes public key from %zd bytes X.509 cert", *publicKeySize,
+             x509CertSize);
+    return true;
+}
+
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+                                     const uint8_t* publicKey, size_t publicKeySize) {
+    vector<uint8_t> certVec(x509Cert, x509Cert + x509CertSize);
+    vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+    return android::hardware::identity::support::certificateSignedByPublicKey(certVec,
+                                                                              publicKeyVec);
+}
+
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+                                    const uint8_t* signature, size_t signatureSize,
+                                    const uint8_t* publicKey, size_t publicKeySize) {
+    vector<uint8_t> digestVec(digest, digest + digestSize);
+    vector<uint8_t> signatureVec(signature, signature + signatureSize);
+    vector<uint8_t> publicKeyVec(publicKey, publicKey + publicKeySize);
+
+    vector<uint8_t> derSignature;
+    if (!android::hardware::identity::support::ecdsaSignatureCoseToDer(signatureVec,
+                                                                       derSignature)) {
+        LOG(ERROR) << "Error convering signature to DER format";
+        return false;
+    }
+
+    if (!android::hardware::identity::support::checkEcDsaSignature(digestVec, derSignature,
+                                                                   publicKeyVec)) {
+        LOG(ERROR) << "Signature check failed";
+        return false;
+    }
+    return true;
+}
+
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                const uint8_t privateKey[EIC_P256_PUB_KEY_SIZE],
+                uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]) {
+    vector<uint8_t> pubKeyVec(EIC_P256_PUB_KEY_SIZE + 1);
+    pubKeyVec[0] = 0x04;
+    memcpy(pubKeyVec.data() + 1, publicKey, EIC_P256_PUB_KEY_SIZE);
+
+    vector<uint8_t> privKeyVec(EIC_P256_PRIV_KEY_SIZE);
+    memcpy(privKeyVec.data(), privateKey, EIC_P256_PRIV_KEY_SIZE);
+
+    optional<vector<uint8_t>> shared =
+            android::hardware::identity::support::ecdh(pubKeyVec, privKeyVec);
+    if (!shared) {
+        LOG(ERROR) << "Error performing ECDH";
+        return false;
+    }
+    if (shared.value().size() != EIC_P256_COORDINATE_SIZE) {
+        LOG(ERROR) << "Unexpected size of shared secret " << shared.value().size() << " expected "
+                   << EIC_P256_COORDINATE_SIZE << " bytes";
+        return false;
+    }
+    memcpy(sharedSecret, shared.value().data(), EIC_P256_COORDINATE_SIZE);
+    return true;
+}
+
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+                size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+                size_t outputSize) {
+    vector<uint8_t> sharedSecretVec(sharedSecretSize);
+    memcpy(sharedSecretVec.data(), sharedSecret, sharedSecretSize);
+    vector<uint8_t> saltVec(saltSize);
+    memcpy(saltVec.data(), salt, saltSize);
+    vector<uint8_t> infoVec(infoSize);
+    memcpy(infoVec.data(), info, infoSize);
+
+    optional<vector<uint8_t>> result = android::hardware::identity::support::hkdf(
+            sharedSecretVec, saltVec, infoVec, outputSize);
+    if (!result) {
+        LOG(ERROR) << "Error performing HKDF";
+        return false;
+    }
+    if (result.value().size() != outputSize) {
+        LOG(ERROR) << "Unexpected size of HKDF " << result.value().size() << " expected "
+                   << outputSize;
+        return false;
+    }
+    memcpy(output, result.value().data(), outputSize);
+    return true;
+}
+
+#ifdef EIC_DEBUG
+
+void eicPrint(const char* format, ...) {
+    va_list args;
+    va_start(args, format);
+    vfprintf(stderr, format, args);
+    va_end(args);
+}
+
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize) {
+    vector<uint8_t> dataVec(dataSize);
+    memcpy(dataVec.data(), data, dataSize);
+    android::hardware::identity::support::hexdump(message, dataVec);
+}
+
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize) {
+    vector<uint8_t> cborDataVec(cborDataSize);
+    memcpy(cborDataVec.data(), cborData, cborDataSize);
+    string str =
+            android::hardware::identity::support::cborPrettyPrint(cborDataVec, maxBStrSize, {});
+    fprintf(stderr, "%s\n", str.c_str());
+}
+
+#endif  // EIC_DEBUG
diff --git a/identity/aidl/default/EicOpsImpl.h b/identity/aidl/default/EicOpsImpl.h
new file mode 100644
index 0000000..333cdce
--- /dev/null
+++ b/identity/aidl/default/EicOpsImpl.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright 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_IDENTITY_EIC_OPS_IMPL_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_IMPL_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Add whatever includes are needed for definitions below.
+//
+
+#include <openssl/hmac.h>
+#include <openssl/sha.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Set the following defines to match the implementation of the supplied
+// eicOps*() operations. See EicOps.h for details.
+//
+
+#define EIC_SHA256_CONTEXT_SIZE sizeof(SHA256_CTX)
+
+#define EIC_HMAC_SHA256_CONTEXT_SIZE sizeof(HMAC_CTX)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EMBEDDED_IC_H
diff --git a/identity/aidl/default/EicTests.cpp b/identity/aidl/default/EicTests.cpp
new file mode 100644
index 0000000..a28080d
--- /dev/null
+++ b/identity/aidl/default/EicTests.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#include <gtest/gtest.h>
+#include <optional>
+#include <string>
+#include <vector>
+
+#include "FakeSecureHardwareProxy.h"
+
+// Most of libeic is tested as part of VTS since there's almost a 1:1 mapping between
+// the HAL and libeic interfaces. This test suite is mainly for the few things which
+// doesn't map directly.
+//
+
+using std::optional;
+using std::string;
+using std::vector;
+
+using android::hardware::identity::AccessCheckResult;
+using android::hardware::identity::FakeSecureHardwarePresentationProxy;
+using android::hardware::identity::FakeSecureHardwareProvisioningProxy;
+
+TEST(EicTest, AccessControlIsEnforced) {
+    // First provision the credential...
+    //
+    FakeSecureHardwareProvisioningProxy provisioningProxy;
+    bool isTestCredential = false;
+    provisioningProxy.initialize(isTestCredential);
+    optional<vector<uint8_t>> credKey =
+            provisioningProxy.createCredentialKey({0x01, 0x02}, {0x03, 0x04});
+    ASSERT_TRUE(credKey.has_value());
+    string docType = "org.iso.18013.5.1.mDL";
+    ASSERT_TRUE(provisioningProxy.startPersonalization(0, {1}, docType, 125));
+
+    vector<int> acpIds = {};
+    string nameSpace = "org.iso.18013.5.1";
+    string name = "NonAccessibleElement";
+    vector<uint8_t> content = {0x63, 0x46, 0x6f, 0x6f};  // "Foo" tstr
+    ASSERT_TRUE(provisioningProxy.beginAddEntry(acpIds, nameSpace, name, content.size()));
+    optional<vector<uint8_t>> encContent =
+            provisioningProxy.addEntryValue(acpIds, nameSpace, name, content);
+    ASSERT_TRUE(encContent.has_value());
+    ASSERT_EQ(encContent->size(), content.size() + 28);
+
+    optional<vector<uint8_t>> signatureOfToBeSigned = provisioningProxy.finishAddingEntries();
+    ASSERT_TRUE(signatureOfToBeSigned.has_value());
+
+    optional<vector<uint8_t>> credData = provisioningProxy.finishGetCredentialData(docType);
+    ASSERT_TRUE(credData.has_value());
+    ASSERT_TRUE(provisioningProxy.shutdown());
+
+    // Then present data from it...
+    //
+    FakeSecureHardwarePresentationProxy presentationProxy;
+    ASSERT_TRUE(presentationProxy.initialize(isTestCredential, docType, credData.value()));
+    AccessCheckResult res =
+            presentationProxy.startRetrieveEntryValue(nameSpace, name, 1, content.size(), acpIds);
+    ASSERT_EQ(res, AccessCheckResult::kNoAccessControlProfiles);
+
+    // Ensure that we can't get the data out if startRetrieveEntryValue() returned
+    // something other than kOk... See b/190757775 for details.
+    //
+    optional<vector<uint8_t>> decContent =
+            presentationProxy.retrieveEntryValue(encContent.value(), nameSpace, name, acpIds);
+    ASSERT_FALSE(decContent.has_value());
+}
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.cpp b/identity/aidl/default/FakeSecureHardwareProxy.cpp
new file mode 100644
index 0000000..287ffb8
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.cpp
@@ -0,0 +1,347 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "FakeSecureHardwareProxy"
+
+#include "FakeSecureHardwareProxy.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+#include <string.h>
+
+#include <openssl/sha.h>
+
+#include <openssl/aes.h>
+#include <openssl/bn.h>
+#include <openssl/crypto.h>
+#include <openssl/ec.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hkdf.h>
+#include <openssl/hmac.h>
+#include <openssl/objects.h>
+#include <openssl/pem.h>
+#include <openssl/pkcs12.h>
+#include <openssl/rand.h>
+#include <openssl/x509.h>
+#include <openssl/x509_vfy.h>
+
+#include <libeic.h>
+
+using ::std::optional;
+using ::std::string;
+using ::std::tuple;
+using ::std::vector;
+
+namespace android::hardware::identity {
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwareProvisioningProxy::FakeSecureHardwareProvisioningProxy() {}
+
+FakeSecureHardwareProvisioningProxy::~FakeSecureHardwareProvisioningProxy() {}
+
+bool FakeSecureHardwareProvisioningProxy::shutdown() {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+    return true;
+}
+
+bool FakeSecureHardwareProvisioningProxy::initialize(bool testCredential) {
+    LOG(INFO) << "FakeSecureHardwareProvisioningProxy created, sizeof(EicProvisioning): "
+              << sizeof(EicProvisioning);
+    return eicProvisioningInit(&ctx_, testCredential);
+}
+
+bool FakeSecureHardwareProvisioningProxy::initializeForUpdate(
+        bool testCredential, string docType, vector<uint8_t> encryptedCredentialKeys) {
+    return eicProvisioningInitForUpdate(&ctx_, testCredential, docType.c_str(),
+                                        encryptedCredentialKeys.data(),
+                                        encryptedCredentialKeys.size());
+}
+
+// Returns public key certificate.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::createCredentialKey(
+        const vector<uint8_t>& challenge, const vector<uint8_t>& applicationId) {
+    uint8_t publicKeyCert[4096];
+    size_t publicKeyCertSize = sizeof publicKeyCert;
+    if (!eicProvisioningCreateCredentialKey(&ctx_, challenge.data(), challenge.size(),
+                                            applicationId.data(), applicationId.size(),
+                                            publicKeyCert, &publicKeyCertSize)) {
+        return {};
+    }
+    vector<uint8_t> pubKeyCert(publicKeyCertSize);
+    memcpy(pubKeyCert.data(), publicKeyCert, publicKeyCertSize);
+    return pubKeyCert;
+}
+
+bool FakeSecureHardwareProvisioningProxy::startPersonalization(
+        int accessControlProfileCount, vector<int> entryCounts, const string& docType,
+        size_t expectedProofOfProvisioningSize) {
+    if (!eicProvisioningStartPersonalization(&ctx_, accessControlProfileCount, entryCounts.data(),
+                                             entryCounts.size(), docType.c_str(),
+                                             expectedProofOfProvisioningSize)) {
+        return false;
+    }
+    return true;
+}
+
+// Returns MAC (28 bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addAccessControlProfile(
+        int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+        uint64_t timeoutMillis, uint64_t secureUserId) {
+    vector<uint8_t> mac(28);
+    if (!eicProvisioningAddAccessControlProfile(
+                &ctx_, id, readerCertificate.data(), readerCertificate.size(),
+                userAuthenticationRequired, timeoutMillis, secureUserId, mac.data())) {
+        return {};
+    }
+    return mac;
+}
+
+bool FakeSecureHardwareProvisioningProxy::beginAddEntry(const vector<int>& accessControlProfileIds,
+                                                        const string& nameSpace, const string& name,
+                                                        uint64_t entrySize) {
+    uint8_t scratchSpace[512];
+    return eicProvisioningBeginAddEntry(&ctx_, accessControlProfileIds.data(),
+                                        accessControlProfileIds.size(), nameSpace.c_str(),
+                                        name.c_str(), entrySize, scratchSpace, sizeof scratchSpace);
+}
+
+// Returns encryptedContent.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::addEntryValue(
+        const vector<int>& accessControlProfileIds, const string& nameSpace, const string& name,
+        const vector<uint8_t>& content) {
+    vector<uint8_t> eicEncryptedContent;
+    uint8_t scratchSpace[512];
+    eicEncryptedContent.resize(content.size() + 28);
+    if (!eicProvisioningAddEntryValue(
+                &ctx_, accessControlProfileIds.data(), accessControlProfileIds.size(),
+                nameSpace.c_str(), name.c_str(), content.data(), content.size(),
+                eicEncryptedContent.data(), scratchSpace, sizeof scratchSpace)) {
+        return {};
+    }
+    return eicEncryptedContent;
+}
+
+// Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishAddingEntries() {
+    vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+    if (!eicProvisioningFinishAddingEntries(&ctx_, signatureOfToBeSigned.data())) {
+        return {};
+    }
+    return signatureOfToBeSigned;
+}
+
+// Returns encryptedCredentialKeys.
+optional<vector<uint8_t>> FakeSecureHardwareProvisioningProxy::finishGetCredentialData(
+        const string& docType) {
+    vector<uint8_t> encryptedCredentialKeys(116);
+    size_t size = encryptedCredentialKeys.size();
+    if (!eicProvisioningFinishGetCredentialData(&ctx_, docType.c_str(),
+                                                encryptedCredentialKeys.data(), &size)) {
+        return {};
+    }
+    encryptedCredentialKeys.resize(size);
+    return encryptedCredentialKeys;
+}
+
+// ----------------------------------------------------------------------
+
+FakeSecureHardwarePresentationProxy::FakeSecureHardwarePresentationProxy() {}
+
+FakeSecureHardwarePresentationProxy::~FakeSecureHardwarePresentationProxy() {}
+
+bool FakeSecureHardwarePresentationProxy::initialize(bool testCredential, string docType,
+                                                     vector<uint8_t> encryptedCredentialKeys) {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy created, sizeof(EicPresentation): "
+              << sizeof(EicPresentation);
+    return eicPresentationInit(&ctx_, testCredential, docType.c_str(),
+                               encryptedCredentialKeys.data(), encryptedCredentialKeys.size());
+}
+
+// Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+optional<pair<vector<uint8_t>, vector<uint8_t>>>
+FakeSecureHardwarePresentationProxy::generateSigningKeyPair(string docType, time_t now) {
+    uint8_t publicKeyCert[512];
+    size_t publicKeyCertSize = sizeof(publicKeyCert);
+    vector<uint8_t> signingKeyBlob(60);
+
+    if (!eicPresentationGenerateSigningKeyPair(&ctx_, docType.c_str(), now, publicKeyCert,
+                                               &publicKeyCertSize, signingKeyBlob.data())) {
+        return {};
+    }
+
+    vector<uint8_t> cert;
+    cert.resize(publicKeyCertSize);
+    memcpy(cert.data(), publicKeyCert, publicKeyCertSize);
+
+    return std::make_pair(cert, signingKeyBlob);
+}
+
+// Returns private key
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::createEphemeralKeyPair() {
+    vector<uint8_t> priv(EIC_P256_PRIV_KEY_SIZE);
+    if (!eicPresentationCreateEphemeralKeyPair(&ctx_, priv.data())) {
+        return {};
+    }
+    return priv;
+}
+
+optional<uint64_t> FakeSecureHardwarePresentationProxy::createAuthChallenge() {
+    uint64_t challenge;
+    if (!eicPresentationCreateAuthChallenge(&ctx_, &challenge)) {
+        return {};
+    }
+    return challenge;
+}
+
+bool FakeSecureHardwarePresentationProxy::shutdown() {
+    LOG(INFO) << "FakeSecureHardwarePresentationProxy shutdown";
+    return true;
+}
+
+bool FakeSecureHardwarePresentationProxy::pushReaderCert(const vector<uint8_t>& certX509) {
+    return eicPresentationPushReaderCert(&ctx_, certX509.data(), certX509.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::validateRequestMessage(
+        const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& requestMessage,
+        int coseSignAlg, const vector<uint8_t>& readerSignatureOfToBeSigned) {
+    return eicPresentationValidateRequestMessage(
+            &ctx_, sessionTranscript.data(), sessionTranscript.size(), requestMessage.data(),
+            requestMessage.size(), coseSignAlg, readerSignatureOfToBeSigned.data(),
+            readerSignatureOfToBeSigned.size());
+}
+
+bool FakeSecureHardwarePresentationProxy::setAuthToken(
+        uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+        int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+        uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+        int verificationTokenSecurityLevel, const vector<uint8_t>& verificationTokenMac) {
+    return eicPresentationSetAuthToken(&ctx_, challenge, secureUserId, authenticatorId,
+                                       hardwareAuthenticatorType, timeStamp, mac.data(), mac.size(),
+                                       verificationTokenChallenge, verificationTokenTimestamp,
+                                       verificationTokenSecurityLevel, verificationTokenMac.data(),
+                                       verificationTokenMac.size());
+}
+
+optional<bool> FakeSecureHardwarePresentationProxy::validateAccessControlProfile(
+        int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+        int timeoutMillis, uint64_t secureUserId, const vector<uint8_t>& mac) {
+    bool accessGranted = false;
+    if (!eicPresentationValidateAccessControlProfile(&ctx_, id, readerCertificate.data(),
+                                                     readerCertificate.size(),
+                                                     userAuthenticationRequired, timeoutMillis,
+                                                     secureUserId, mac.data(), &accessGranted)) {
+        return {};
+    }
+    return accessGranted;
+}
+
+bool FakeSecureHardwarePresentationProxy::startRetrieveEntries() {
+    return eicPresentationStartRetrieveEntries(&ctx_);
+}
+
+bool FakeSecureHardwarePresentationProxy::calcMacKey(
+        const vector<uint8_t>& sessionTranscript, const vector<uint8_t>& readerEphemeralPublicKey,
+        const vector<uint8_t>& signingKeyBlob, const string& docType,
+        unsigned int numNamespacesWithValues, size_t expectedProofOfProvisioningSize) {
+    if (signingKeyBlob.size() != 60) {
+        eicDebug("Unexpected size %zd of signingKeyBlob, expected 60", signingKeyBlob.size());
+        return false;
+    }
+    return eicPresentationCalcMacKey(&ctx_, sessionTranscript.data(), sessionTranscript.size(),
+                                     readerEphemeralPublicKey.data(), signingKeyBlob.data(),
+                                     docType.c_str(), numNamespacesWithValues,
+                                     expectedProofOfProvisioningSize);
+}
+
+AccessCheckResult FakeSecureHardwarePresentationProxy::startRetrieveEntryValue(
+        const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+        int32_t entrySize, const vector<int32_t>& accessControlProfileIds) {
+    uint8_t scratchSpace[512];
+    EicAccessCheckResult result = eicPresentationStartRetrieveEntryValue(
+            &ctx_, nameSpace.c_str(), name.c_str(), newNamespaceNumEntries, entrySize,
+            accessControlProfileIds.data(), accessControlProfileIds.size(), scratchSpace,
+            sizeof scratchSpace);
+    switch (result) {
+        case EIC_ACCESS_CHECK_RESULT_OK:
+            return AccessCheckResult::kOk;
+        case EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES:
+            return AccessCheckResult::kNoAccessControlProfiles;
+        case EIC_ACCESS_CHECK_RESULT_FAILED:
+            return AccessCheckResult::kFailed;
+        case EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED:
+            return AccessCheckResult::kUserAuthenticationFailed;
+        case EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED:
+            return AccessCheckResult::kReaderAuthenticationFailed;
+    }
+    eicDebug("Unknown result with code %d, returning kFailed", (int)result);
+    return AccessCheckResult::kFailed;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::retrieveEntryValue(
+        const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+        const vector<int32_t>& accessControlProfileIds) {
+    uint8_t scratchSpace[512];
+    vector<uint8_t> content;
+    content.resize(encryptedContent.size() - 28);
+    if (!eicPresentationRetrieveEntryValue(
+                &ctx_, encryptedContent.data(), encryptedContent.size(), content.data(),
+                nameSpace.c_str(), name.c_str(), accessControlProfileIds.data(),
+                accessControlProfileIds.size(), scratchSpace, sizeof scratchSpace)) {
+        return {};
+    }
+    return content;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::finishRetrieval() {
+    vector<uint8_t> mac(32);
+    size_t macSize = 32;
+    if (!eicPresentationFinishRetrieval(&ctx_, mac.data(), &macSize)) {
+        return {};
+    }
+    mac.resize(macSize);
+    return mac;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::deleteCredential(
+        const string& docType, const vector<uint8_t>& challenge, bool includeChallenge,
+        size_t proofOfDeletionCborSize) {
+    vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+    if (!eicPresentationDeleteCredential(&ctx_, docType.c_str(), challenge.data(), challenge.size(),
+                                         includeChallenge, proofOfDeletionCborSize,
+                                         signatureOfToBeSigned.data())) {
+        return {};
+    }
+    return signatureOfToBeSigned;
+}
+
+optional<vector<uint8_t>> FakeSecureHardwarePresentationProxy::proveOwnership(
+        const string& docType, bool testCredential, const vector<uint8_t>& challenge,
+        size_t proofOfOwnershipCborSize) {
+    vector<uint8_t> signatureOfToBeSigned(EIC_ECDSA_P256_SIGNATURE_SIZE);
+    if (!eicPresentationProveOwnership(&ctx_, docType.c_str(), testCredential, challenge.data(),
+                                       challenge.size(), proofOfOwnershipCborSize,
+                                       signatureOfToBeSigned.data())) {
+        return {};
+    }
+    return signatureOfToBeSigned;
+}
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/default/FakeSecureHardwareProxy.h b/identity/aidl/default/FakeSecureHardwareProxy.h
new file mode 100644
index 0000000..6852c1a
--- /dev/null
+++ b/identity/aidl/default/FakeSecureHardwareProxy.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 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_IDENTITY_FAKESECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
+
+#include <libeic/libeic.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace android::hardware::identity {
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwareProvisioningProxy : public SecureHardwareProvisioningProxy {
+  public:
+    FakeSecureHardwareProvisioningProxy();
+    virtual ~FakeSecureHardwareProvisioningProxy();
+
+    bool initialize(bool testCredential) override;
+
+    bool initializeForUpdate(bool testCredential, string docType,
+                             vector<uint8_t> encryptedCredentialKeys) override;
+
+    bool shutdown() override;
+
+    // Returns public key certificate.
+    optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+                                                  const vector<uint8_t>& applicationId) override;
+
+    bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+                              const string& docType,
+                              size_t expectedProofOfProvisioningSize) override;
+
+    // Returns MAC (28 bytes).
+    optional<vector<uint8_t>> addAccessControlProfile(int id,
+                                                      const vector<uint8_t>& readerCertificate,
+                                                      bool userAuthenticationRequired,
+                                                      uint64_t timeoutMillis,
+                                                      uint64_t secureUserId) override;
+
+    bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+                       const string& name, uint64_t entrySize) override;
+
+    // Returns encryptedContent.
+    optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+                                            const string& nameSpace, const string& name,
+                                            const vector<uint8_t>& content) override;
+
+    // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+    optional<vector<uint8_t>> finishAddingEntries() override;
+
+    // Returns encryptedCredentialKeys (80 bytes).
+    optional<vector<uint8_t>> finishGetCredentialData(const string& docType) override;
+
+  protected:
+    EicProvisioning ctx_;
+};
+
+// This implementation uses libEmbeddedIC in-process.
+//
+class FakeSecureHardwarePresentationProxy : public SecureHardwarePresentationProxy {
+  public:
+    FakeSecureHardwarePresentationProxy();
+    virtual ~FakeSecureHardwarePresentationProxy();
+
+    bool initialize(bool testCredential, string docType,
+                    vector<uint8_t> encryptedCredentialKeys) override;
+
+    // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+    optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+                                                                            time_t now) override;
+
+    // Returns private key
+    optional<vector<uint8_t>> createEphemeralKeyPair() override;
+
+    optional<uint64_t> createAuthChallenge() override;
+
+    bool startRetrieveEntries() override;
+
+    bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                      int hardwareAuthenticatorType, uint64_t timeStamp, const vector<uint8_t>& mac,
+                      uint64_t verificationTokenChallenge, uint64_t verificationTokenTimestamp,
+                      int verificationTokenSecurityLevel,
+                      const vector<uint8_t>& verificationTokenMac) override;
+
+    bool pushReaderCert(const vector<uint8_t>& certX509) override;
+
+    optional<bool> validateAccessControlProfile(int id, const vector<uint8_t>& readerCertificate,
+                                                bool userAuthenticationRequired, int timeoutMillis,
+                                                uint64_t secureUserId,
+                                                const vector<uint8_t>& mac) override;
+
+    bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+                                const vector<uint8_t>& requestMessage, int coseSignAlg,
+                                const vector<uint8_t>& readerSignatureOfToBeSigned) override;
+
+    bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+                    const vector<uint8_t>& readerEphemeralPublicKey,
+                    const vector<uint8_t>& signingKeyBlob, const string& docType,
+                    unsigned int numNamespacesWithValues,
+                    size_t expectedProofOfProvisioningSize) override;
+
+    AccessCheckResult startRetrieveEntryValue(
+            const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+            int32_t entrySize, const vector<int32_t>& accessControlProfileIds) override;
+
+    optional<vector<uint8_t>> retrieveEntryValue(
+            const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+            const vector<int32_t>& accessControlProfileIds) override;
+
+    optional<vector<uint8_t>> finishRetrieval() override;
+
+    optional<vector<uint8_t>> deleteCredential(const string& docType,
+                                               const vector<uint8_t>& challenge,
+                                               bool includeChallenge,
+                                               size_t proofOfDeletionCborSize) override;
+
+    optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+                                             const vector<uint8_t>& challenge,
+                                             size_t proofOfOwnershipCborSize) override;
+
+    bool shutdown() override;
+
+  protected:
+    EicPresentation ctx_;
+};
+
+// Factory implementation.
+//
+class FakeSecureHardwareProxyFactory : public SecureHardwareProxyFactory {
+  public:
+    FakeSecureHardwareProxyFactory() {}
+    virtual ~FakeSecureHardwareProxyFactory() {}
+
+    sp<SecureHardwareProvisioningProxy> createProvisioningProxy() override {
+        return new FakeSecureHardwareProvisioningProxy();
+    }
+
+    sp<SecureHardwarePresentationProxy> createPresentationProxy() override {
+        return new FakeSecureHardwarePresentationProxy();
+    }
+};
+
+}  // namespace android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_FAKESECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/IdentityCredential.cpp b/identity/aidl/default/IdentityCredential.cpp
deleted file mode 100644
index 41d410d..0000000
--- a/identity/aidl/default/IdentityCredential.cpp
+++ /dev/null
@@ -1,869 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#define LOG_TAG "IdentityCredential"
-
-#include "IdentityCredential.h"
-#include "IdentityCredentialStore.h"
-#include "Util.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::aidl::android::hardware::keymaster::Timestamp;
-using ::android::base::StringPrintf;
-using ::std::optional;
-
-using namespace ::android::hardware::identity;
-
-int IdentityCredential::initialize() {
-    if (credentialData_.size() == 0) {
-        LOG(ERROR) << "CredentialData is empty";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    auto [item, _, message] = cppbor::parse(credentialData_);
-    if (item == nullptr) {
-        LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-
-    const cppbor::Array* arrayItem = item->asArray();
-    if (arrayItem == nullptr || arrayItem->size() != 3) {
-        LOG(ERROR) << "CredentialData is not an array with three elements";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-
-    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
-    const cppbor::Bool* testCredentialItem =
-            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
-                                                    : nullptr);
-    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
-    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
-        encryptedCredentialKeysItem == nullptr) {
-        LOG(ERROR) << "CredentialData unexpected item types";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-
-    docType_ = docTypeItem->value();
-    testCredential_ = testCredentialItem->value();
-
-    vector<uint8_t> hardwareBoundKey;
-    if (testCredential_) {
-        hardwareBoundKey = support::getTestHardwareBoundKey();
-    } else {
-        hardwareBoundKey = getHardwareBoundKey();
-    }
-
-    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-    const vector<uint8_t> docTypeVec(docType_.begin(), docType_.end());
-    optional<vector<uint8_t>> decryptedCredentialKeys =
-            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
-    if (!decryptedCredentialKeys) {
-        LOG(ERROR) << "Error decrypting CredentialKeys";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-
-    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
-    if (dckItem == nullptr) {
-        LOG(ERROR) << "Decrypted CredentialKeys is not valid CBOR: " << dckMessage;
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    const cppbor::Array* dckArrayItem = dckItem->asArray();
-    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
-        LOG(ERROR) << "Decrypted CredentialKeys is not an array with two elements";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
-    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
-    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
-        LOG(ERROR) << "CredentialKeys unexpected item types";
-        return IIdentityCredentialStore::STATUS_INVALID_DATA;
-    }
-    storageKey_ = storageKeyItem->value();
-    credentialPrivKey_ = credentialPrivKeyItem->value();
-
-    return IIdentityCredentialStore::STATUS_OK;
-}
-
-ndk::ScopedAStatus IdentityCredential::deleteCredential(
-        vector<int8_t>* outProofOfDeletionSignature) {
-    cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
-    vector<uint8_t> proofOfDeletion = array.encode();
-
-    optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
-                                                                 proofOfDeletion,  // payload
-                                                                 {},               // additionalData
-                                                                 {});  // certificateChain
-    if (!signature) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
-    }
-
-    *outProofOfDeletionSignature = byteStringToSigned(signature.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<int8_t>* outKeyPair) {
-    optional<vector<uint8_t>> kp = support::createEcKeyPair();
-    if (!kp) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key pair"));
-    }
-
-    // Stash public key of this key-pair for later check in startRetrieval().
-    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(kp.value());
-    if (!publicKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of ephemeral key pair"));
-    }
-    ephemeralPublicKey_ = publicKey.value();
-
-    *outKeyPair = byteStringToSigned(kp.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
-        const vector<int8_t>& publicKey) {
-    readerPublicKey_ = byteStringToUnsigned(publicKey);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
-    uint64_t challenge = 0;
-    while (challenge == 0) {
-        optional<vector<uint8_t>> bytes = support::getRandom(8);
-        if (!bytes) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED,
-                    "Error getting random data for challenge"));
-        }
-
-        challenge = 0;
-        for (size_t n = 0; n < bytes.value().size(); n++) {
-            challenge |= ((bytes.value())[n] << (n * 8));
-        }
-    }
-
-    *outChallenge = challenge;
-    authChallenge_ = challenge;
-    return ndk::ScopedAStatus::ok();
-}
-
-// TODO: this could be a lot faster if we did all the splitting and pubkey extraction
-// ahead of time.
-bool checkReaderAuthentication(const SecureAccessControlProfile& profile,
-                               const vector<uint8_t>& readerCertificateChain) {
-    optional<vector<uint8_t>> acpPubKey = support::certificateChainGetTopMostKey(
-            byteStringToUnsigned(profile.readerCertificate.encodedCertificate));
-    if (!acpPubKey) {
-        LOG(ERROR) << "Error extracting public key from readerCertificate in profile";
-        return false;
-    }
-
-    optional<vector<vector<uint8_t>>> certificatesInChain =
-            support::certificateChainSplit(readerCertificateChain);
-    if (!certificatesInChain) {
-        LOG(ERROR) << "Error splitting readerCertificateChain";
-        return false;
-    }
-    for (const vector<uint8_t>& certInChain : certificatesInChain.value()) {
-        optional<vector<uint8_t>> certPubKey = support::certificateChainGetTopMostKey(certInChain);
-        if (!certPubKey) {
-            LOG(ERROR)
-                    << "Error extracting public key from certificate in chain presented by reader";
-            return false;
-        }
-        if (acpPubKey.value() == certPubKey.value()) {
-            return true;
-        }
-    }
-    return false;
-}
-
-bool checkUserAuthentication(const SecureAccessControlProfile& profile,
-                             const VerificationToken& verificationToken,
-                             const HardwareAuthToken& authToken, uint64_t authChallenge) {
-    if (profile.secureUserId != authToken.userId) {
-        LOG(ERROR) << "secureUserId in profile (" << profile.secureUserId
-                   << ") differs from userId in authToken (" << authToken.userId << ")";
-        return false;
-    }
-
-    if (verificationToken.timestamp.milliSeconds == 0) {
-        LOG(ERROR) << "VerificationToken is not set";
-        return false;
-    }
-    if (authToken.timestamp.milliSeconds == 0) {
-        LOG(ERROR) << "AuthToken is not set";
-        return false;
-    }
-
-    if (profile.timeoutMillis == 0) {
-        if (authToken.challenge == 0) {
-            LOG(ERROR) << "No challenge in authToken";
-            return false;
-        }
-
-        if (authToken.challenge != int64_t(authChallenge)) {
-            LOG(ERROR) << "Challenge in authToken (" << uint64_t(authToken.challenge) << ") "
-                       << "doesn't match the challenge we created (" << authChallenge << ")";
-            return false;
-        }
-        return true;
-    }
-
-    // Timeout-based user auth follows. The verification token conveys what the
-    // time is right now in the environment which generated the auth token. This
-    // is what makes it possible to do timeout-based checks.
-    //
-    const Timestamp now = verificationToken.timestamp;
-    if (authToken.timestamp.milliSeconds > now.milliSeconds) {
-        LOG(ERROR) << "Timestamp in authToken (" << authToken.timestamp.milliSeconds
-                   << ") is in the future (now: " << now.milliSeconds << ")";
-        return false;
-    }
-    if (now.milliSeconds > authToken.timestamp.milliSeconds + profile.timeoutMillis) {
-        LOG(ERROR) << "Deadline for authToken (" << authToken.timestamp.milliSeconds << " + "
-                   << profile.timeoutMillis << " = "
-                   << (authToken.timestamp.milliSeconds + profile.timeoutMillis)
-                   << ") is in the past (now: " << now.milliSeconds << ")";
-        return false;
-    }
-    return true;
-}
-
-ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
-        const vector<RequestNamespace>& requestNamespaces) {
-    requestNamespaces_ = requestNamespaces;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::setVerificationToken(
-        const VerificationToken& verificationToken) {
-    verificationToken_ = verificationToken;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::startRetrieval(
-        const vector<SecureAccessControlProfile>& accessControlProfiles,
-        const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequestS,
-        const vector<int8_t>& signingKeyBlobS, const vector<int8_t>& sessionTranscriptS,
-        const vector<int8_t>& readerSignatureS, const vector<int32_t>& requestCounts) {
-    auto sessionTranscript = byteStringToUnsigned(sessionTranscriptS);
-    auto itemsRequest = byteStringToUnsigned(itemsRequestS);
-    auto readerSignature = byteStringToUnsigned(readerSignatureS);
-
-    std::unique_ptr<cppbor::Item> sessionTranscriptItem;
-    if (sessionTranscript.size() > 0) {
-        auto [item, _, message] = cppbor::parse(sessionTranscript);
-        if (item == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "SessionTranscript contains invalid CBOR"));
-        }
-        sessionTranscriptItem = std::move(item);
-    }
-    if (numStartRetrievalCalls_ > 0) {
-        if (sessionTranscript_ != sessionTranscript) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
-                    "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
-        }
-    }
-    sessionTranscript_ = sessionTranscript;
-
-    // If there is a signature, validate that it was made with the top-most key in the
-    // certificate chain embedded in the COSE_Sign1 structure.
-    optional<vector<uint8_t>> readerCertificateChain;
-    if (readerSignature.size() > 0) {
-        readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
-        if (!readerCertificateChain) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "Unable to get reader certificate chain from COSE_Sign1"));
-        }
-
-        if (!support::certificateChainValidate(readerCertificateChain.value())) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "Error validating reader certificate chain"));
-        }
-
-        optional<vector<uint8_t>> readerPublicKey =
-                support::certificateChainGetTopMostKey(readerCertificateChain.value());
-        if (!readerPublicKey) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "Unable to get public key from reader certificate chain"));
-        }
-
-        const vector<uint8_t>& itemsRequestBytes = itemsRequest;
-        vector<uint8_t> encodedReaderAuthentication =
-                cppbor::Array()
-                        .add("ReaderAuthentication")
-                        .add(std::move(sessionTranscriptItem))
-                        .add(cppbor::Semantic(24, itemsRequestBytes))
-                        .encode();
-        vector<uint8_t> encodedReaderAuthenticationBytes =
-                cppbor::Semantic(24, encodedReaderAuthentication).encode();
-        if (!support::coseCheckEcDsaSignature(readerSignature,
-                                              encodedReaderAuthenticationBytes,  // detached content
-                                              readerPublicKey.value())) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
-                    "readerSignature check failed"));
-        }
-    }
-
-    // Here's where we would validate the passed-in |authToken| to assure ourselves
-    // that it comes from the e.g. biometric hardware and wasn't made up by an attacker.
-    //
-    // However this involves calculating the MAC. However this requires access
-    // to the key needed to a pre-shared key which we don't have...
-    //
-
-    // To prevent replay-attacks, we check that the public part of the ephemeral
-    // key we previously created, is present in the DeviceEngagement part of
-    // SessionTranscript as a COSE_Key, in uncompressed form.
-    //
-    // We do this by just searching for the X and Y coordinates.
-    if (sessionTranscript.size() > 0) {
-        auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
-        if (!getXYSuccess) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
-                    "Error extracting X and Y from ePub"));
-        }
-        if (sessionTranscript.size() > 0 &&
-            !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
-                     ePubX.size()) != nullptr &&
-              memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
-                     ePubY.size()) != nullptr)) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
-                    "Did not find ephemeral public key's X and Y coordinates in "
-                    "SessionTranscript (make sure leading zeroes are not used)"));
-        }
-    }
-
-    // itemsRequest: If non-empty, contains request data that may be signed by the
-    // reader.  The content can be defined in the way appropriate for the
-    // credential, but there are three requirements that must be met to work with
-    // this HAL:
-    if (itemsRequest.size() > 0) {
-        // 1. The content must be a CBOR-encoded structure.
-        auto [item, _, message] = cppbor::parse(itemsRequest);
-        if (item == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
-                    "Error decoding CBOR in itemsRequest"));
-        }
-
-        // 2. The CBOR structure must be a map.
-        const cppbor::Map* map = item->asMap();
-        if (map == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
-                    "itemsRequest is not a CBOR map"));
-        }
-
-        // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
-        //    the example below.
-        //
-        //   NameSpaces = {
-        //     + NameSpace => DataElements ; Requested data elements for each NameSpace
-        //   }
-        //
-        //   NameSpace = tstr
-        //
-        //   DataElements = {
-        //     + DataElement => IntentToRetain
-        //   }
-        //
-        //   DataElement = tstr
-        //   IntentToRetain = bool
-        //
-        // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
-        // through 3.:
-        //
-        //    {
-        //        'docType' : 'org.iso.18013-5.2019',
-        //        'nameSpaces' : {
-        //            'org.iso.18013-5.2019' : {
-        //                'Last name' : false,
-        //                'Birth date' : false,
-        //                'First name' : false,
-        //                'Home address' : true
-        //            },
-        //            'org.aamva.iso.18013-5.2019' : {
-        //                'Real Id' : false
-        //            }
-        //        }
-        //    }
-        //
-        const cppbor::Map* nsMap = nullptr;
-        for (size_t n = 0; n < map->size(); n++) {
-            const auto& [keyItem, valueItem] = (*map)[n];
-            if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
-                valueItem->type() == cppbor::MAP) {
-                nsMap = valueItem->asMap();
-                break;
-            }
-        }
-        if (nsMap == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
-                    "No nameSpaces map in top-most map"));
-        }
-
-        for (size_t n = 0; n < nsMap->size(); n++) {
-            auto [nsKeyItem, nsValueItem] = (*nsMap)[n];
-            const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
-            const cppbor::Map* nsInnerMap = nsValueItem->asMap();
-            if (nsKey == nullptr || nsInnerMap == nullptr) {
-                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                        IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
-                        "Type mismatch in nameSpaces map"));
-            }
-            string requestedNamespace = nsKey->value();
-            set<string> requestedKeys;
-            for (size_t m = 0; m < nsInnerMap->size(); m++) {
-                const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
-                const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
-                const cppbor::Simple* simple = innerMapValueItem->asSimple();
-                const cppbor::Bool* intentToRetainItem =
-                        (simple != nullptr) ? simple->asBool() : nullptr;
-                if (nameItem == nullptr || intentToRetainItem == nullptr) {
-                    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                            IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
-                            "Type mismatch in value in nameSpaces map"));
-                }
-                requestedKeys.insert(nameItem->value());
-            }
-            requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
-        }
-    }
-
-    // Validate all the access control profiles in the requestData.
-    bool haveAuthToken = (authToken.timestamp.milliSeconds != int64_t(0));
-    for (const auto& profile : accessControlProfiles) {
-        if (!secureAccessControlProfileCheckMac(profile, storageKey_)) {
-            LOG(ERROR) << "Error checking MAC for profile";
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Error checking MAC for profile"));
-        }
-        int accessControlCheck = IIdentityCredentialStore::STATUS_OK;
-        if (profile.userAuthenticationRequired) {
-            if (!haveAuthToken ||
-                !checkUserAuthentication(profile, verificationToken_, authToken, authChallenge_)) {
-                accessControlCheck = IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED;
-            }
-        } else if (profile.readerCertificate.encodedCertificate.size() > 0) {
-            if (!readerCertificateChain ||
-                !checkReaderAuthentication(profile, readerCertificateChain.value())) {
-                accessControlCheck = IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED;
-            }
-        }
-        profileIdToAccessCheckResult_[profile.id] = accessControlCheck;
-    }
-
-    deviceNameSpacesMap_ = cppbor::Map();
-    currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
-    requestCountsRemaining_ = requestCounts;
-    currentNameSpace_ = "";
-
-    itemsRequest_ = itemsRequest;
-    signingKeyBlob_ = byteStringToUnsigned(signingKeyBlobS);
-
-    // Finally, calculate the size of DeviceNameSpaces. We need to know it ahead of time.
-    expectedDeviceNameSpacesSize_ = calcDeviceNameSpacesSize();
-
-    numStartRetrievalCalls_ += 1;
-    return ndk::ScopedAStatus::ok();
-}
-
-size_t cborNumBytesForLength(size_t length) {
-    if (length < 24) {
-        return 0;
-    } else if (length <= 0xff) {
-        return 1;
-    } else if (length <= 0xffff) {
-        return 2;
-    } else if (length <= 0xffffffff) {
-        return 4;
-    }
-    return 8;
-}
-
-size_t cborNumBytesForTstr(const string& value) {
-    return 1 + cborNumBytesForLength(value.size()) + value.size();
-}
-
-size_t IdentityCredential::calcDeviceNameSpacesSize() {
-    /*
-     * This is how DeviceNameSpaces is defined:
-     *
-     *        DeviceNameSpaces = {
-     *            * NameSpace => DeviceSignedItems
-     *        }
-     *        DeviceSignedItems = {
-     *            + DataItemName => DataItemValue
-     *        }
-     *
-     *        Namespace = tstr
-     *        DataItemName = tstr
-     *        DataItemValue = any
-     *
-     * This function will calculate its length using knowledge of how CBOR is
-     * encoded.
-     */
-    size_t ret = 0;
-    size_t numNamespacesWithValues = 0;
-    for (const RequestNamespace& rns : requestNamespaces_) {
-        vector<RequestDataItem> itemsToInclude;
-
-        for (const RequestDataItem& rdi : rns.items) {
-            // If we have a CBOR request message, skip if item isn't in it
-            if (itemsRequest_.size() > 0) {
-                const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
-                if (it == requestedNameSpacesAndNames_.end()) {
-                    continue;
-                }
-                const set<string>& dataItemNames = it->second;
-                if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
-                    continue;
-                }
-            }
-
-            // Access is granted if at least one of the profiles grants access.
-            //
-            // If an item is configured without any profiles, access is denied.
-            //
-            bool authorized = false;
-            for (auto id : rdi.accessControlProfileIds) {
-                auto it = profileIdToAccessCheckResult_.find(id);
-                if (it != profileIdToAccessCheckResult_.end()) {
-                    int accessControlForProfile = it->second;
-                    if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
-                        authorized = true;
-                        break;
-                    }
-                }
-            }
-            if (!authorized) {
-                continue;
-            }
-
-            itemsToInclude.push_back(rdi);
-        }
-
-        // If no entries are to be in the namespace, we don't include it...
-        if (itemsToInclude.size() == 0) {
-            continue;
-        }
-
-        // Key: NameSpace
-        ret += cborNumBytesForTstr(rns.namespaceName);
-
-        // Value: Open the DeviceSignedItems map
-        ret += 1 + cborNumBytesForLength(itemsToInclude.size());
-
-        for (const RequestDataItem& item : itemsToInclude) {
-            // Key: DataItemName
-            ret += cborNumBytesForTstr(item.name);
-
-            // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use
-            // that.
-            ret += item.size;
-        }
-
-        numNamespacesWithValues++;
-    }
-
-    // Now that we now the nunber of namespaces with values, we know how many
-    // bytes the DeviceNamespaces map in the beginning is going to take up.
-    ret += 1 + cborNumBytesForLength(numNamespacesWithValues);
-
-    return ret;
-}
-
-ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
-        const string& nameSpace, const string& name, int32_t entrySize,
-        const vector<int32_t>& accessControlProfileIds) {
-    if (name.empty()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
-    }
-    if (nameSpace.empty()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
-    }
-
-    if (requestCountsRemaining_.size() == 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "No more name spaces left to go through"));
-    }
-
-    if (currentNameSpace_ == "") {
-        // First call.
-        currentNameSpace_ = nameSpace;
-    }
-
-    if (nameSpace == currentNameSpace_) {
-        // Same namespace.
-        if (requestCountsRemaining_[0] == 0) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "No more entries to be retrieved in current name space"));
-        }
-        requestCountsRemaining_[0] -= 1;
-    } else {
-        // New namespace.
-        if (requestCountsRemaining_[0] != 0) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Moved to new name space but one or more entries need to be retrieved "
-                    "in current name space"));
-        }
-        if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
-            deviceNameSpacesMap_.add(currentNameSpace_,
-                                     std::move(currentNameSpaceDeviceNameSpacesMap_));
-        }
-        currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
-
-        requestCountsRemaining_.erase(requestCountsRemaining_.begin());
-        currentNameSpace_ = nameSpace;
-    }
-
-    // It's permissible to have an empty itemsRequest... but if non-empty you can
-    // only request what was specified in said itemsRequest. Enforce that.
-    if (itemsRequest_.size() > 0) {
-        const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
-        if (it == requestedNameSpacesAndNames_.end()) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
-                    "Name space was not requested in startRetrieval"));
-        }
-        const set<string>& dataItemNames = it->second;
-        if (dataItemNames.find(name) == dataItemNames.end()) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
-                    "Data item name in name space was not requested in startRetrieval"));
-        }
-    }
-
-    // Enforce access control.
-    //
-    // Access is granted if at least one of the profiles grants access.
-    //
-    // If an item is configured without any profiles, access is denied.
-    //
-    int accessControl = IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES;
-    for (auto id : accessControlProfileIds) {
-        auto search = profileIdToAccessCheckResult_.find(id);
-        if (search == profileIdToAccessCheckResult_.end()) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Requested entry with unvalidated profile id"));
-        }
-        int accessControlForProfile = search->second;
-        if (accessControlForProfile == IIdentityCredentialStore::STATUS_OK) {
-            accessControl = IIdentityCredentialStore::STATUS_OK;
-            break;
-        }
-        accessControl = accessControlForProfile;
-    }
-    if (accessControl != IIdentityCredentialStore::STATUS_OK) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                int(accessControl), "Access control check failed"));
-    }
-
-    entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
-    currentName_ = name;
-    entryRemainingBytes_ = entrySize;
-    entryValue_.resize(0);
-
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<int8_t>& encryptedContentS,
-                                                          vector<int8_t>* outContent) {
-    auto encryptedContent = byteStringToUnsigned(encryptedContentS);
-    optional<vector<uint8_t>> content =
-            support::decryptAes128Gcm(storageKey_, encryptedContent, entryAdditionalData_);
-    if (!content) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
-    }
-
-    size_t chunkSize = content.value().size();
-
-    if (chunkSize > entryRemainingBytes_) {
-        LOG(ERROR) << "Retrieved chunk of size " << chunkSize
-                   << " is bigger than remaining space of size " << entryRemainingBytes_;
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "Retrieved chunk is bigger than remaining space"));
-    }
-
-    entryRemainingBytes_ -= chunkSize;
-    if (entryRemainingBytes_ > 0) {
-        if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
-        }
-    }
-
-    entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
-
-    if (entryRemainingBytes_ == 0) {
-        auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
-        if (entryValueItem == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Retrieved data which is invalid CBOR"));
-        }
-        currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
-    }
-
-    *outContent = byteStringToSigned(content.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<int8_t>* outMac,
-                                                       vector<int8_t>* outDeviceNameSpaces) {
-    if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
-        deviceNameSpacesMap_.add(currentNameSpace_,
-                                 std::move(currentNameSpaceDeviceNameSpacesMap_));
-    }
-    vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
-
-    if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
-        LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, "
-                   << "was expecting " << expectedDeviceNameSpacesSize_;
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                StringPrintf(
-                        "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd",
-                        encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_)
-                        .c_str()));
-    }
-
-    // If there's no signing key or no sessionTranscript or no reader ephemeral
-    // public key, we return the empty MAC.
-    optional<vector<uint8_t>> mac;
-    if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
-        readerPublicKey_.size() > 0) {
-        vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
-        optional<vector<uint8_t>> signingKey =
-                support::decryptAes128Gcm(storageKey_, signingKeyBlob_, docTypeAsBlob);
-        if (!signingKey) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Error decrypting signingKeyBlob"));
-        }
-
-        vector<uint8_t> sessionTranscriptBytes = cppbor::Semantic(24, sessionTranscript_).encode();
-        optional<vector<uint8_t>> eMacKey =
-                support::calcEMacKey(signingKey.value(), readerPublicKey_, sessionTranscriptBytes);
-        if (!eMacKey) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED, "Error calculating EMacKey"));
-        }
-        mac = support::calcMac(sessionTranscript_, docType_, encodedDeviceNameSpaces,
-                               eMacKey.value());
-        if (!mac) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_FAILED, "Error MACing data"));
-        }
-    }
-
-    *outMac = byteStringToSigned(mac.value_or(vector<uint8_t>({})));
-    *outDeviceNameSpaces = byteStringToSigned(encodedDeviceNameSpaces);
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
-        vector<int8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
-    string serialDecimal = "1";
-    string issuer = "Android Identity Credential Key";
-    string subject = "Android Identity Credential Authentication Key";
-    time_t validityNotBefore = time(nullptr);
-    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
-    optional<vector<uint8_t>> signingKeyPKCS8 = support::createEcKeyPair();
-    if (!signingKeyPKCS8) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
-    }
-
-    optional<vector<uint8_t>> signingPublicKey =
-            support::ecKeyPairGetPublicKey(signingKeyPKCS8.value());
-    if (!signingPublicKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of signingKey"));
-    }
-
-    optional<vector<uint8_t>> signingKey = support::ecKeyPairGetPrivateKey(signingKeyPKCS8.value());
-    if (!signingKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting private part of signingKey"));
-    }
-
-    optional<vector<uint8_t>> certificate = support::ecPublicKeyGenerateCertificate(
-            signingPublicKey.value(), credentialPrivKey_, serialDecimal, issuer, subject,
-            validityNotBefore, validityNotAfter);
-    if (!certificate) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
-    }
-
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error getting random"));
-    }
-    vector<uint8_t> docTypeAsBlob(docType_.begin(), docType_.end());
-    optional<vector<uint8_t>> encryptedSigningKey = support::encryptAes128Gcm(
-            storageKey_, nonce.value(), signingKey.value(), docTypeAsBlob);
-    if (!encryptedSigningKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error encrypting signingKey"));
-    }
-    *outSigningKeyBlob = byteStringToSigned(encryptedSigningKey.value());
-    *outSigningKeyCertificate = Certificate();
-    outSigningKeyCertificate->encodedCertificate = byteStringToSigned(certificate.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredential.h b/identity/aidl/default/IdentityCredential.h
deleted file mode 100644
index f44d731..0000000
--- a/identity/aidl/default/IdentityCredential.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2019, 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_IDENTITY_IDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
-
-#include <aidl/android/hardware/identity/BnIdentityCredential.h>
-#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
-#include <aidl/android/hardware/keymaster/VerificationToken.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::aidl::android::hardware::keymaster::HardwareAuthToken;
-using ::aidl::android::hardware::keymaster::VerificationToken;
-using ::std::map;
-using ::std::set;
-using ::std::string;
-using ::std::vector;
-
-class IdentityCredential : public BnIdentityCredential {
-  public:
-    IdentityCredential(const vector<uint8_t>& credentialData)
-        : credentialData_(credentialData),
-          numStartRetrievalCalls_(0),
-          authChallenge_(0),
-          expectedDeviceNameSpacesSize_(0) {}
-
-    // Parses and decrypts credentialData_, return a status code from
-    // IIdentityCredentialStore. Must be called right after construction.
-    int initialize();
-
-    // Methods from IIdentityCredential follow.
-    ndk::ScopedAStatus deleteCredential(vector<int8_t>* outProofOfDeletionSignature) override;
-    ndk::ScopedAStatus createEphemeralKeyPair(vector<int8_t>* outKeyPair) override;
-    ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<int8_t>& publicKey) override;
-    ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
-    ndk::ScopedAStatus setRequestedNamespaces(
-            const vector<RequestNamespace>& requestNamespaces) override;
-    ndk::ScopedAStatus setVerificationToken(const VerificationToken& verificationToken) override;
-    ndk::ScopedAStatus startRetrieval(
-            const vector<SecureAccessControlProfile>& accessControlProfiles,
-            const HardwareAuthToken& authToken, const vector<int8_t>& itemsRequest,
-            const vector<int8_t>& signingKeyBlob, const vector<int8_t>& sessionTranscript,
-            const vector<int8_t>& readerSignature, const vector<int32_t>& requestCounts) override;
-    ndk::ScopedAStatus startRetrieveEntryValue(
-            const string& nameSpace, const string& name, int32_t entrySize,
-            const vector<int32_t>& accessControlProfileIds) override;
-    ndk::ScopedAStatus retrieveEntryValue(const vector<int8_t>& encryptedContent,
-                                          vector<int8_t>* outContent) override;
-    ndk::ScopedAStatus finishRetrieval(vector<int8_t>* outMac,
-                                       vector<int8_t>* outDeviceNameSpaces) override;
-    ndk::ScopedAStatus generateSigningKeyPair(vector<int8_t>* outSigningKeyBlob,
-                                              Certificate* outSigningKeyCertificate) override;
-
-  private:
-    // Set by constructor
-    vector<uint8_t> credentialData_;
-    int numStartRetrievalCalls_;
-
-    // Set by initialize()
-    string docType_;
-    bool testCredential_;
-    vector<uint8_t> storageKey_;
-    vector<uint8_t> credentialPrivKey_;
-
-    // Set by createEphemeralKeyPair()
-    vector<uint8_t> ephemeralPublicKey_;
-
-    // Set by setReaderEphemeralPublicKey()
-    vector<uint8_t> readerPublicKey_;
-
-    // Set by createAuthChallenge()
-    uint64_t authChallenge_;
-
-    // Set by setRequestedNamespaces()
-    vector<RequestNamespace> requestNamespaces_;
-
-    // Set by setVerificationToken().
-    VerificationToken verificationToken_;
-
-    // Set at startRetrieval() time.
-    map<int32_t, int> profileIdToAccessCheckResult_;
-    vector<uint8_t> signingKeyBlob_;
-    vector<uint8_t> sessionTranscript_;
-    vector<uint8_t> itemsRequest_;
-    vector<int32_t> requestCountsRemaining_;
-    map<string, set<string>> requestedNameSpacesAndNames_;
-    cppbor::Map deviceNameSpacesMap_;
-    cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
-
-    // Calculated at startRetrieval() time.
-    size_t expectedDeviceNameSpacesSize_;
-
-    // Set at startRetrieveEntryValue() time.
-    string currentNameSpace_;
-    string currentName_;
-    size_t entryRemainingBytes_;
-    vector<uint8_t> entryValue_;
-    vector<uint8_t> entryAdditionalData_;
-
-    size_t calcDeviceNameSpacesSize();
-};
-
-}  // namespace aidl::android::hardware::identity
-
-#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/IdentityCredentialStore.cpp b/identity/aidl/default/IdentityCredentialStore.cpp
deleted file mode 100644
index 1efb4b4..0000000
--- a/identity/aidl/default/IdentityCredentialStore.cpp
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#define LOG_TAG "IdentityCredentialStore"
-
-#include <android-base/logging.h>
-
-#include "IdentityCredential.h"
-#include "IdentityCredentialStore.h"
-#include "WritableIdentityCredential.h"
-
-namespace aidl::android::hardware::identity {
-
-ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
-        HardwareInformation* hardwareInformation) {
-    HardwareInformation hw;
-    hw.credentialStoreName = "Identity Credential Reference Implementation";
-    hw.credentialStoreAuthorName = "Google";
-    hw.dataChunkSize = kGcmChunkSize;
-    hw.isDirectAccess = false;
-    hw.supportedDocTypes = {};
-    *hardwareInformation = hw;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredentialStore::createCredential(
-        const string& docType, bool testCredential,
-        shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
-    shared_ptr<WritableIdentityCredential> wc =
-            ndk::SharedRefBase::make<WritableIdentityCredential>(docType, testCredential);
-    if (!wc->initialize()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error initializing WritableIdentityCredential"));
-    }
-    *outWritableCredential = wc;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus IdentityCredentialStore::getCredential(
-        CipherSuite cipherSuite, const vector<int8_t>& credentialData,
-        shared_ptr<IIdentityCredential>* outCredential) {
-    // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
-    if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
-                "Unsupported cipher suite"));
-    }
-
-    vector<uint8_t> data = vector<uint8_t>(credentialData.begin(), credentialData.end());
-    shared_ptr<IdentityCredential> credential = ndk::SharedRefBase::make<IdentityCredential>(data);
-    auto ret = credential->initialize();
-    if (ret != IIdentityCredentialStore::STATUS_OK) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                int(ret), "Error initializing IdentityCredential"));
-    }
-    *outCredential = credential;
-    return ndk::ScopedAStatus::ok();
-}
-
-}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/IdentityCredentialStore.h b/identity/aidl/default/IdentityCredentialStore.h
deleted file mode 100644
index a205113..0000000
--- a/identity/aidl/default/IdentityCredentialStore.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2019, 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_IDENTITY_IDENTITYCREDENTIALSTORE_H
-#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
-
-#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::std::shared_ptr;
-using ::std::string;
-using ::std::vector;
-
-class IdentityCredentialStore : public BnIdentityCredentialStore {
-  public:
-    IdentityCredentialStore() {}
-
-    // The GCM chunk size used by this implementation is 64 KiB.
-    static constexpr size_t kGcmChunkSize = 64 * 1024;
-
-    // Methods from IIdentityCredentialStore follow.
-    ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
-
-    ndk::ScopedAStatus createCredential(
-            const string& docType, bool testCredential,
-            shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
-
-    ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<int8_t>& credentialData,
-                                     shared_ptr<IIdentityCredential>* outCredential) override;
-};
-
-}  // namespace aidl::android::hardware::identity
-
-#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/aidl/default/Util.cpp b/identity/aidl/default/Util.cpp
deleted file mode 100644
index a0f86be..0000000
--- a/identity/aidl/default/Util.cpp
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#define LOG_TAG "Util"
-
-#include "Util.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <string.h>
-
-#include <android-base/logging.h>
-
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace aidl::android::hardware::identity {
-
-using namespace ::android::hardware::identity;
-
-// This is not a very random HBK but that's OK because this is the SW
-// implementation where it can't be kept secret.
-vector<uint8_t> hardwareBoundKey = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-const vector<uint8_t>& getHardwareBoundKey() {
-    return hardwareBoundKey;
-}
-
-vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value) {
-    return vector<uint8_t>(value.begin(), value.end());
-}
-
-vector<int8_t> byteStringToSigned(const vector<uint8_t>& value) {
-    return vector<int8_t>(value.begin(), value.end());
-}
-
-vector<uint8_t> secureAccessControlProfileEncodeCbor(const SecureAccessControlProfile& profile) {
-    cppbor::Map map;
-    map.add("id", profile.id);
-
-    if (profile.readerCertificate.encodedCertificate.size() > 0) {
-        map.add("readerCertificate",
-                cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
-    }
-
-    if (profile.userAuthenticationRequired) {
-        map.add("userAuthenticationRequired", profile.userAuthenticationRequired);
-        map.add("timeoutMillis", profile.timeoutMillis);
-        map.add("secureUserId", profile.secureUserId);
-    }
-
-    return map.encode();
-}
-
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
-        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey) {
-    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return {};
-    }
-    optional<vector<uint8_t>> macO =
-            support::encryptAes128Gcm(storageKey, nonce.value(), {}, cborData);
-    if (!macO) {
-        return {};
-    }
-    return macO.value();
-}
-
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
-                                        const vector<uint8_t>& storageKey) {
-    vector<uint8_t> cborData = secureAccessControlProfileEncodeCbor(profile);
-
-    if (profile.mac.size() < support::kAesGcmIvSize) {
-        return false;
-    }
-    vector<uint8_t> nonce =
-            vector<uint8_t>(profile.mac.begin(), profile.mac.begin() + support::kAesGcmIvSize);
-    optional<vector<uint8_t>> mac = support::encryptAes128Gcm(storageKey, nonce, {}, cborData);
-    if (!mac) {
-        return false;
-    }
-    if (mac.value() != byteStringToUnsigned(profile.mac)) {
-        return false;
-    }
-    return true;
-}
-
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
-                                          const vector<int32_t> accessControlProfileIds) {
-    cppbor::Map map;
-    map.add("Namespace", nameSpace);
-    map.add("Name", name);
-
-    cppbor::Array acpIds;
-    for (auto id : accessControlProfileIds) {
-        acpIds.add(id);
-    }
-    map.add("AccessControlProfileIds", std::move(acpIds));
-    return map.encode();
-}
-
-}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/Util.h b/identity/aidl/default/Util.h
deleted file mode 100644
index ee41ad1..0000000
--- a/identity/aidl/default/Util.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2019, 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_IDENTITY_UTIL_H
-#define ANDROID_HARDWARE_IDENTITY_UTIL_H
-
-#include <aidl/android/hardware/identity/BnIdentityCredential.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <map>
-#include <optional>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <cppbor/cppbor.h>
-
-namespace aidl::android::hardware::identity {
-
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-// Returns the hardware-bound AES-128 key.
-const vector<uint8_t>& getHardwareBoundKey();
-
-// Calculates the MAC for |profile| using |storageKey|.
-optional<vector<uint8_t>> secureAccessControlProfileCalcMac(
-        const SecureAccessControlProfile& profile, const vector<uint8_t>& storageKey);
-
-// Checks authenticity of the MAC in |profile| using |storageKey|.
-bool secureAccessControlProfileCheckMac(const SecureAccessControlProfile& profile,
-                                        const vector<uint8_t>& storageKey);
-
-// Creates the AdditionalData CBOR used in the addEntryValue() HIDL method.
-vector<uint8_t> entryCreateAdditionalData(const string& nameSpace, const string& name,
-                                          const vector<int32_t> accessControlProfileIds);
-
-vector<uint8_t> byteStringToUnsigned(const vector<int8_t>& value);
-
-vector<int8_t> byteStringToSigned(const vector<uint8_t>& value);
-
-}  // namespace aidl::android::hardware::identity
-
-#endif  // ANDROID_HARDWARE_IDENTITY_UTIL_H
diff --git a/identity/aidl/default/WritableIdentityCredential.cpp b/identity/aidl/default/WritableIdentityCredential.cpp
deleted file mode 100644
index 4428543..0000000
--- a/identity/aidl/default/WritableIdentityCredential.cpp
+++ /dev/null
@@ -1,444 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#define LOG_TAG "WritableIdentityCredential"
-
-#include "WritableIdentityCredential.h"
-#include "IdentityCredentialStore.h"
-
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <android-base/logging.h>
-#include <android-base/stringprintf.h>
-
-#include <cppbor/cppbor.h>
-#include <cppbor/cppbor_parse.h>
-
-#include <utility>
-
-#include "IdentityCredentialStore.h"
-#include "Util.h"
-#include "WritableIdentityCredential.h"
-
-namespace aidl::android::hardware::identity {
-
-using ::android::base::StringPrintf;
-using ::std::optional;
-using namespace ::android::hardware::identity;
-
-bool WritableIdentityCredential::initialize() {
-    optional<vector<uint8_t>> random = support::getRandom(16);
-    if (!random) {
-        LOG(ERROR) << "Error creating storageKey";
-        return false;
-    }
-    storageKey_ = random.value();
-    startPersonalizationCalled_ = false;
-    firstEntry_ = true;
-
-    return true;
-}
-
-// This function generates the attestation certificate using the passed in
-// |attestationApplicationId| and |attestationChallenge|.  It will generate an
-// attestation certificate with current time and expires one year from now.  The
-// certificate shall contain all values as specified in hal.
-ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
-        const vector<int8_t>& attestationApplicationId,  //
-        const vector<int8_t>& attestationChallenge,      //
-        vector<Certificate>* outCertificateChain) {
-    if (!credentialPrivKey_.empty() || !credentialPubKey_.empty() || !certificateChain_.empty()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error attestation certificate previously generated"));
-    }
-    if (attestationChallenge.empty()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
-    }
-
-    vector<uint8_t> challenge(attestationChallenge.begin(), attestationChallenge.end());
-    vector<uint8_t> appId(attestationApplicationId.begin(), attestationApplicationId.end());
-
-    optional<std::pair<vector<uint8_t>, vector<vector<uint8_t>>>> keyAttestationPair =
-            support::createEcKeyPairAndAttestation(challenge, appId, testCredential_);
-    if (!keyAttestationPair) {
-        LOG(ERROR) << "Error creating credentialKey and attestation";
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error creating credentialKey and attestation"));
-    }
-
-    vector<uint8_t> keyPair = keyAttestationPair.value().first;
-    certificateChain_ = keyAttestationPair.value().second;
-
-    optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair);
-    if (!pubKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting public part of credentialKey"));
-    }
-    credentialPubKey_ = pubKey.value();
-
-    optional<vector<uint8_t>> privKey = support::ecKeyPairGetPrivateKey(keyPair);
-    if (!privKey) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED,
-                "Error getting private part of credentialKey"));
-    }
-    credentialPrivKey_ = privKey.value();
-
-    // convert from vector<vector<uint8_t>>> to vector<Certificate>*
-    *outCertificateChain = vector<Certificate>();
-    for (const vector<uint8_t>& cert : certificateChain_) {
-        Certificate c = Certificate();
-        c.encodedCertificate = byteStringToSigned(cert);
-        outCertificateChain->push_back(std::move(c));
-    }
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
-        int32_t expectedProofOfProvisioningSize) {
-    expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
-        int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
-    if (startPersonalizationCalled_) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
-    }
-
-    startPersonalizationCalled_ = true;
-    numAccessControlProfileRemaining_ = accessControlProfileCount;
-    remainingEntryCounts_ = entryCounts;
-    entryNameSpace_ = "";
-
-    signedDataAccessControlProfiles_ = cppbor::Array();
-    signedDataNamespaces_ = cppbor::Map();
-    signedDataCurrentNamespace_ = cppbor::Array();
-
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
-        int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
-        int64_t timeoutMillis, int64_t secureUserId,
-        SecureAccessControlProfile* outSecureAccessControlProfile) {
-    SecureAccessControlProfile profile;
-
-    if (numAccessControlProfileRemaining_ == 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
-    }
-
-    if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "Access Control Profile id must be unique"));
-    }
-    accessControlProfileIds_.insert(id);
-
-    if (id < 0 || id >= 32) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "Access Control Profile id must be non-negative and less than 32"));
-    }
-
-    // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
-    // be zero.
-    if (!userAuthenticationRequired && timeoutMillis != 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "userAuthenticationRequired is false but timeout is non-zero"));
-    }
-
-    // If |userAuthenticationRequired| is true, then |secureUserId| must be non-zero.
-    if (userAuthenticationRequired && secureUserId == 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "userAuthenticationRequired is true but secureUserId is zero"));
-    }
-
-    profile.id = id;
-    profile.readerCertificate = readerCertificate;
-    profile.userAuthenticationRequired = userAuthenticationRequired;
-    profile.timeoutMillis = timeoutMillis;
-    profile.secureUserId = secureUserId;
-    optional<vector<uint8_t>> mac = secureAccessControlProfileCalcMac(profile, storageKey_);
-    if (!mac) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error calculating MAC for profile"));
-    }
-    profile.mac = byteStringToSigned(mac.value());
-
-    cppbor::Map profileMap;
-    profileMap.add("id", profile.id);
-    if (profile.readerCertificate.encodedCertificate.size() > 0) {
-        profileMap.add(
-                "readerCertificate",
-                cppbor::Bstr(byteStringToUnsigned(profile.readerCertificate.encodedCertificate)));
-    }
-    if (profile.userAuthenticationRequired) {
-        profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
-        profileMap.add("timeoutMillis", profile.timeoutMillis);
-    }
-    signedDataAccessControlProfiles_.add(std::move(profileMap));
-
-    numAccessControlProfileRemaining_--;
-
-    *outSecureAccessControlProfile = profile;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
-        const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
-        int32_t entrySize) {
-    if (numAccessControlProfileRemaining_ != 0) {
-        LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
-                   << " and expected zero";
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "numAccessControlProfileRemaining_ is not zero"));
-    }
-
-    if (remainingEntryCounts_.size() == 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
-    }
-
-    // Handle initial beginEntry() call.
-    if (firstEntry_) {
-        firstEntry_ = false;
-        entryNameSpace_ = nameSpace;
-        allNameSpaces_.insert(nameSpace);
-    }
-
-    // If the namespace changed...
-    if (nameSpace != entryNameSpace_) {
-        if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Name space cannot be added in interleaving fashion"));
-        }
-
-        // Then check that all entries in the previous namespace have been added..
-        if (remainingEntryCounts_[0] != 0) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "New namespace but a non-zero number of entries remain to be added"));
-        }
-        remainingEntryCounts_.erase(remainingEntryCounts_.begin());
-        remainingEntryCounts_[0] -= 1;
-        allNameSpaces_.insert(nameSpace);
-
-        if (signedDataCurrentNamespace_.size() > 0) {
-            signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
-            signedDataCurrentNamespace_ = cppbor::Array();
-        }
-    } else {
-        // Same namespace...
-        if (remainingEntryCounts_[0] == 0) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Same namespace but no entries remain to be added"));
-        }
-        remainingEntryCounts_[0] -= 1;
-    }
-
-    entryAdditionalData_ = entryCreateAdditionalData(nameSpace, name, accessControlProfileIds);
-
-    entryRemainingBytes_ = entrySize;
-    entryNameSpace_ = nameSpace;
-    entryName_ = name;
-    entryAccessControlProfileIds_ = accessControlProfileIds;
-    entryBytes_.resize(0);
-    // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
-    return ndk::ScopedAStatus::ok();
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<int8_t>& contentS,
-                                                             vector<int8_t>* outEncryptedContentS) {
-    auto content = byteStringToUnsigned(contentS);
-    size_t contentSize = content.size();
-
-    if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "Passed in chunk of is bigger than kGcmChunkSize"));
-    }
-    if (contentSize > entryRemainingBytes_) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "Passed in chunk is bigger than remaining space"));
-    }
-
-    entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
-    entryRemainingBytes_ -= contentSize;
-    if (entryRemainingBytes_ > 0) {
-        if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA,
-                    "Retrieved non-final chunk which isn't kGcmChunkSize"));
-        }
-    }
-
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error getting nonce"));
-    }
-    optional<vector<uint8_t>> encryptedContent =
-            support::encryptAes128Gcm(storageKey_, nonce.value(), content, entryAdditionalData_);
-    if (!encryptedContent) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error encrypting content"));
-    }
-
-    if (entryRemainingBytes_ == 0) {
-        // TODO: ideally do do this without parsing the data (but still validate data is valid
-        // CBOR).
-        auto [item, _, message] = cppbor::parse(entryBytes_);
-        if (item == nullptr) {
-            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                    IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
-        }
-        cppbor::Map entryMap;
-        entryMap.add("name", entryName_);
-        entryMap.add("value", std::move(item));
-        cppbor::Array profileIdArray;
-        for (auto id : entryAccessControlProfileIds_) {
-            profileIdArray.add(id);
-        }
-        entryMap.add("accessControlProfiles", std::move(profileIdArray));
-        signedDataCurrentNamespace_.add(std::move(entryMap));
-    }
-
-    *outEncryptedContentS = byteStringToSigned(encryptedContent.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-// Writes CBOR-encoded structure to |credentialKeys| containing |storageKey| and
-// |credentialPrivKey|.
-static bool generateCredentialKeys(const vector<uint8_t>& storageKey,
-                                   const vector<uint8_t>& credentialPrivKey,
-                                   vector<uint8_t>& credentialKeys) {
-    if (storageKey.size() != 16) {
-        LOG(ERROR) << "Size of storageKey is not 16";
-        return false;
-    }
-
-    cppbor::Array array;
-    array.add(cppbor::Bstr(storageKey));
-    array.add(cppbor::Bstr(credentialPrivKey));
-    credentialKeys = array.encode();
-    return true;
-}
-
-// Writes CBOR-encoded structure to |credentialData| containing |docType|,
-// |testCredential| and |credentialKeys|. The latter element will be stored in
-// encrypted form, using |hardwareBoundKey| as the encryption key.
-bool generateCredentialData(const vector<uint8_t>& hardwareBoundKey, const string& docType,
-                            bool testCredential, const vector<uint8_t>& credentialKeys,
-                            vector<uint8_t>& credentialData) {
-    optional<vector<uint8_t>> nonce = support::getRandom(12);
-    if (!nonce) {
-        LOG(ERROR) << "Error getting random";
-        return false;
-    }
-    vector<uint8_t> docTypeAsVec(docType.begin(), docType.end());
-    optional<vector<uint8_t>> credentialBlob = support::encryptAes128Gcm(
-            hardwareBoundKey, nonce.value(), credentialKeys, docTypeAsVec);
-    if (!credentialBlob) {
-        LOG(ERROR) << "Error encrypting CredentialKeys blob";
-        return false;
-    }
-
-    cppbor::Array array;
-    array.add(docType);
-    array.add(testCredential);
-    array.add(cppbor::Bstr(credentialBlob.value()));
-    credentialData = array.encode();
-    return true;
-}
-
-ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
-        vector<int8_t>* outCredentialData, vector<int8_t>* outProofOfProvisioningSignature) {
-    if (numAccessControlProfileRemaining_ != 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "numAccessControlProfileRemaining_ is not 0 and expected zero"));
-    }
-
-    if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                "More entry spaces remain than startPersonalization configured"));
-    }
-
-    if (signedDataCurrentNamespace_.size() > 0) {
-        signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
-    }
-    cppbor::Array popArray;
-    popArray.add("ProofOfProvisioning")
-            .add(docType_)
-            .add(std::move(signedDataAccessControlProfiles_))
-            .add(std::move(signedDataNamespaces_))
-            .add(testCredential_);
-    vector<uint8_t> encodedCbor = popArray.encode();
-
-    if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
-        LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
-                   << "was expecting " << expectedProofOfProvisioningSize_;
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_INVALID_DATA,
-                StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
-                             encodedCbor.size(), expectedProofOfProvisioningSize_)
-                        .c_str()));
-    }
-
-    optional<vector<uint8_t>> signature = support::coseSignEcDsa(credentialPrivKey_,
-                                                                 encodedCbor,  // payload
-                                                                 {},           // additionalData
-                                                                 {});          // certificateChain
-    if (!signature) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
-    }
-
-    vector<uint8_t> credentialKeys;
-    if (!generateCredentialKeys(storageKey_, credentialPrivKey_, credentialKeys)) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialKeys"));
-    }
-
-    vector<uint8_t> credentialData;
-    if (!generateCredentialData(
-                testCredential_ ? support::getTestHardwareBoundKey() : getHardwareBoundKey(),
-                docType_, testCredential_, credentialKeys, credentialData)) {
-        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
-                IIdentityCredentialStore::STATUS_FAILED, "Error generating CredentialData"));
-    }
-
-    *outCredentialData = byteStringToSigned(credentialData);
-    *outProofOfProvisioningSignature = byteStringToSigned(signature.value());
-    return ndk::ScopedAStatus::ok();
-}
-
-}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/WritableIdentityCredential.h b/identity/aidl/default/WritableIdentityCredential.h
deleted file mode 100644
index 05104d7..0000000
--- a/identity/aidl/default/WritableIdentityCredential.h
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * Copyright 2019, 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_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
-
-#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-
-#include <cppbor.h>
-#include <set>
-
-namespace aidl::android::hardware::identity {
-
-using ::std::set;
-using ::std::string;
-using ::std::vector;
-
-class WritableIdentityCredential : public BnWritableIdentityCredential {
-  public:
-    WritableIdentityCredential(const string& docType, bool testCredential)
-        : docType_(docType), testCredential_(testCredential) {}
-
-    // Creates the Credential Key. Returns false on failure. Must be called
-    // right after construction.
-    bool initialize();
-
-    // Methods from IWritableIdentityCredential follow.
-    ndk::ScopedAStatus getAttestationCertificate(const vector<int8_t>& attestationApplicationId,
-                                                 const vector<int8_t>& attestationChallenge,
-                                                 vector<Certificate>* outCertificateChain) override;
-
-    ndk::ScopedAStatus setExpectedProofOfProvisioningSize(
-            int32_t expectedProofOfProvisioningSize) override;
-
-    ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
-                                            const vector<int32_t>& entryCounts) override;
-
-    ndk::ScopedAStatus addAccessControlProfile(
-            int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
-            int64_t timeoutMillis, int64_t secureUserId,
-            SecureAccessControlProfile* outSecureAccessControlProfile) override;
-
-    ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
-                                     const string& nameSpace, const string& name,
-                                     int32_t entrySize) override;
-
-    ndk::ScopedAStatus addEntryValue(const vector<int8_t>& content,
-                                     vector<int8_t>* outEncryptedContent) override;
-
-    ndk::ScopedAStatus finishAddingEntries(
-            vector<int8_t>* outCredentialData,
-            vector<int8_t>* outProofOfProvisioningSignature) override;
-
-  private:
-    string docType_;
-    bool testCredential_;
-
-    // This is set in initialize().
-    vector<uint8_t> storageKey_;
-    bool startPersonalizationCalled_;
-    bool firstEntry_;
-
-    // These are set in getAttestationCertificate().
-    vector<uint8_t> credentialPrivKey_;
-    vector<uint8_t> credentialPubKey_;
-    vector<vector<uint8_t>> certificateChain_;
-
-    // These fields are initialized during startPersonalization()
-    size_t numAccessControlProfileRemaining_;
-    vector<int32_t> remainingEntryCounts_;
-    cppbor::Array signedDataAccessControlProfiles_;
-    cppbor::Map signedDataNamespaces_;
-    cppbor::Array signedDataCurrentNamespace_;
-    size_t expectedProofOfProvisioningSize_;
-
-    // This field is initialized in addAccessControlProfile
-    set<int32_t> accessControlProfileIds_;
-
-    // These fields are initialized during beginAddEntry()
-    size_t entryRemainingBytes_;
-    vector<uint8_t> entryAdditionalData_;
-    string entryNameSpace_;
-    string entryName_;
-    vector<int32_t> entryAccessControlProfileIds_;
-    vector<uint8_t> entryBytes_;
-    set<string> allNameSpaces_;
-};
-
-}  // namespace aidl::android::hardware::identity
-
-#endif  // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/android.hardware.identity_credential.xml b/identity/aidl/default/android.hardware.identity_credential.xml
new file mode 100644
index 0000000..5149792
--- /dev/null
+++ b/identity/aidl/default/android.hardware.identity_credential.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<permissions>
+  <feature name="android.hardware.identity_credential" version="202101" />
+</permissions>
diff --git a/identity/aidl/default/common/IdentityCredential.cpp b/identity/aidl/default/common/IdentityCredential.cpp
new file mode 100644
index 0000000..95557b5
--- /dev/null
+++ b/identity/aidl/default/common/IdentityCredential.cpp
@@ -0,0 +1,901 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#define LOG_TAG "IdentityCredential"
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <string.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include "FakeSecureHardwareProxy.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::Timestamp;
+using ::android::base::StringPrintf;
+using ::std::optional;
+
+using namespace ::android::hardware::identity;
+
+int IdentityCredential::initialize() {
+    if (credentialData_.size() == 0) {
+        LOG(ERROR) << "CredentialData is empty";
+        return IIdentityCredentialStore::STATUS_INVALID_DATA;
+    }
+    auto [item, _, message] = cppbor::parse(credentialData_);
+    if (item == nullptr) {
+        LOG(ERROR) << "CredentialData is not valid CBOR: " << message;
+        return IIdentityCredentialStore::STATUS_INVALID_DATA;
+    }
+
+    const cppbor::Array* arrayItem = item->asArray();
+    if (arrayItem == nullptr || arrayItem->size() != 3) {
+        LOG(ERROR) << "CredentialData is not an array with three elements";
+        return IIdentityCredentialStore::STATUS_INVALID_DATA;
+    }
+
+    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+    const cppbor::Bool* testCredentialItem =
+            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+                                                    : nullptr);
+    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+        encryptedCredentialKeysItem == nullptr) {
+        LOG(ERROR) << "CredentialData unexpected item types";
+        return IIdentityCredentialStore::STATUS_INVALID_DATA;
+    }
+
+    docType_ = docTypeItem->value();
+    testCredential_ = testCredentialItem->value();
+
+    encryptedCredentialKeys_ = encryptedCredentialKeysItem->value();
+    if (!hwProxy_->initialize(testCredential_, docType_, encryptedCredentialKeys_)) {
+        LOG(ERROR) << "hwProxy->initialize failed";
+        return false;
+    }
+
+    return IIdentityCredentialStore::STATUS_OK;
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredential(
+        vector<uint8_t>* outProofOfDeletionSignature) {
+    return deleteCredentialCommon({}, false, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialWithChallenge(
+        const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfDeletionSignature) {
+    return deleteCredentialCommon(challenge, true, outProofOfDeletionSignature);
+}
+
+ndk::ScopedAStatus IdentityCredential::deleteCredentialCommon(
+        const vector<uint8_t>& challenge, bool includeChallenge,
+        vector<uint8_t>* outProofOfDeletionSignature) {
+    if (challenge.size() > 32) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+    }
+
+    cppbor::Array array = {"ProofOfDeletion", docType_, testCredential_};
+    if (includeChallenge) {
+        array = {"ProofOfDeletion", docType_, challenge, testCredential_};
+    }
+
+    vector<uint8_t> proofOfDeletionCbor = array.encode();
+    vector<uint8_t> podDigest = support::sha256(proofOfDeletionCbor);
+
+    optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->deleteCredential(
+            docType_, challenge, includeChallenge, proofOfDeletionCbor.size());
+    if (!signatureOfToBeSigned) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfDeletion"));
+    }
+
+    optional<vector<uint8_t>> signature =
+            support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+                                                proofOfDeletionCbor,  // data
+                                                {});                  // certificateChain
+    if (!signature) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+    }
+
+    *outProofOfDeletionSignature = signature.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::proveOwnership(
+        const vector<uint8_t>& challenge, vector<uint8_t>* outProofOfOwnershipSignature) {
+    if (challenge.size() > 32) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge too big"));
+    }
+
+    cppbor::Array array;
+    array = {"ProofOfOwnership", docType_, challenge, testCredential_};
+    vector<uint8_t> proofOfOwnershipCbor = array.encode();
+    vector<uint8_t> podDigest = support::sha256(proofOfOwnershipCbor);
+
+    optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->proveOwnership(
+            docType_, testCredential_, challenge, proofOfOwnershipCbor.size());
+    if (!signatureOfToBeSigned) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing ProofOfOwnership"));
+    }
+
+    optional<vector<uint8_t>> signature =
+            support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+                                                proofOfOwnershipCbor,  // data
+                                                {});                   // certificateChain
+    if (!signature) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+    }
+
+    *outProofOfOwnershipSignature = signature.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createEphemeralKeyPair(vector<uint8_t>* outKeyPair) {
+    optional<vector<uint8_t>> ephemeralPriv = hwProxy_->createEphemeralKeyPair();
+    if (!ephemeralPriv) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key"));
+    }
+    optional<vector<uint8_t>> keyPair = support::ecPrivateKeyToKeyPair(ephemeralPriv.value());
+    if (!keyPair) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error creating ephemeral key-pair"));
+    }
+
+    // Stash public key of this key-pair for later check in startRetrieval().
+    optional<vector<uint8_t>> publicKey = support::ecKeyPairGetPublicKey(keyPair.value());
+    if (!publicKey) {
+        LOG(ERROR) << "Error getting public part of ephemeral key pair";
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error getting public part of ephemeral key pair"));
+    }
+    ephemeralPublicKey_ = publicKey.value();
+
+    *outKeyPair = keyPair.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setReaderEphemeralPublicKey(
+        const vector<uint8_t>& publicKey) {
+    readerPublicKey_ = publicKey;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::createAuthChallenge(int64_t* outChallenge) {
+    optional<uint64_t> challenge = hwProxy_->createAuthChallenge();
+    if (!challenge) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error generating challenge"));
+    }
+    *outChallenge = challenge.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setRequestedNamespaces(
+        const vector<RequestNamespace>& requestNamespaces) {
+    requestNamespaces_ = requestNamespaces;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::setVerificationToken(
+        const VerificationToken& verificationToken) {
+    verificationToken_ = verificationToken;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieval(
+        const vector<SecureAccessControlProfile>& accessControlProfiles,
+        const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
+        const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
+        const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) {
+    std::unique_ptr<cppbor::Item> sessionTranscriptItem;
+    if (sessionTranscript.size() > 0) {
+        auto [item, _, message] = cppbor::parse(sessionTranscript);
+        if (item == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "SessionTranscript contains invalid CBOR"));
+        }
+        sessionTranscriptItem = std::move(item);
+    }
+    if (numStartRetrievalCalls_ > 0) {
+        if (sessionTranscript_ != sessionTranscript) {
+            LOG(ERROR) << "Session Transcript changed";
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_SESSION_TRANSCRIPT_MISMATCH,
+                    "Passed-in SessionTranscript doesn't match previously used SessionTranscript"));
+        }
+    }
+    sessionTranscript_ = sessionTranscript;
+
+    // This resets various state in the TA...
+    if (!hwProxy_->startRetrieveEntries()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+    }
+
+    optional<vector<uint8_t>> signatureOfToBeSigned;
+    if (readerSignature.size() > 0) {
+        signatureOfToBeSigned = support::coseSignGetSignature(readerSignature);
+        if (!signatureOfToBeSigned) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                    "Error extracting signatureOfToBeSigned from COSE_Sign1"));
+        }
+    }
+
+    // Feed the auth token to secure hardware only if they're valid.
+    if (authToken.timestamp.milliSeconds != 0) {
+        if (!hwProxy_->setAuthToken(
+                    authToken.challenge, authToken.userId, authToken.authenticatorId,
+                    int(authToken.authenticatorType), authToken.timestamp.milliSeconds,
+                    authToken.mac, verificationToken_.challenge,
+                    verificationToken_.timestamp.milliSeconds,
+                    int(verificationToken_.securityLevel), verificationToken_.mac)) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA, "Invalid Auth Token"));
+        }
+    }
+
+    // We'll be feeding ACPs interleaved with certificates from the reader
+    // certificate chain...
+    vector<SecureAccessControlProfile> remainingAcps = accessControlProfiles;
+
+    // ... and we'll use those ACPs to build up a 32-bit mask indicating which
+    // of the possible 32 ACPs grants access.
+    uint32_t accessControlProfileMask = 0;
+
+    // If there is a signature, validate that it was made with the top-most key in the
+    // certificate chain embedded in the COSE_Sign1 structure.
+    optional<vector<uint8_t>> readerCertificateChain;
+    if (readerSignature.size() > 0) {
+        readerCertificateChain = support::coseSignGetX5Chain(readerSignature);
+        if (!readerCertificateChain) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                    "Unable to get reader certificate chain from COSE_Sign1"));
+        }
+
+        // First, feed all the reader certificates to the secure hardware. We start
+        // at the end..
+        optional<vector<vector<uint8_t>>> splitCerts =
+                support::certificateChainSplit(readerCertificateChain.value());
+        if (!splitCerts || splitCerts.value().size() == 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                    "Error splitting certificate chain from COSE_Sign1"));
+        }
+        for (ssize_t n = splitCerts.value().size() - 1; n >= 0; --n) {
+            const vector<uint8_t>& x509Cert = splitCerts.value()[n];
+            if (!hwProxy_->pushReaderCert(x509Cert)) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        StringPrintf("Error validating reader certificate %zd", n).c_str()));
+            }
+
+            // If we have ACPs for that particular certificate, send them to the
+            // TA right now...
+            //
+            // Remember in this case certificate equality is done by comparing public keys,
+            // not bitwise comparison of the certificates.
+            //
+            optional<vector<uint8_t>> x509CertPubKey =
+                    support::certificateChainGetTopMostKey(x509Cert);
+            if (!x509CertPubKey) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_FAILED,
+                        StringPrintf("Error getting public key from reader certificate %zd", n)
+                                .c_str()));
+            }
+            vector<SecureAccessControlProfile>::iterator it = remainingAcps.begin();
+            while (it != remainingAcps.end()) {
+                const SecureAccessControlProfile& profile = *it;
+                if (profile.readerCertificate.encodedCertificate.size() == 0) {
+                    ++it;
+                    continue;
+                }
+                optional<vector<uint8_t>> profilePubKey = support::certificateChainGetTopMostKey(
+                        profile.readerCertificate.encodedCertificate);
+                if (!profilePubKey) {
+                    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                            IIdentityCredentialStore::STATUS_FAILED,
+                            "Error getting public key from profile"));
+                }
+                if (profilePubKey.value() == x509CertPubKey.value()) {
+                    optional<bool> res = hwProxy_->validateAccessControlProfile(
+                            profile.id, profile.readerCertificate.encodedCertificate,
+                            profile.userAuthenticationRequired, profile.timeoutMillis,
+                            profile.secureUserId, profile.mac);
+                    if (!res) {
+                        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                                "Error validating access control profile"));
+                    }
+                    if (res.value()) {
+                        accessControlProfileMask |= (1 << profile.id);
+                    }
+                    it = remainingAcps.erase(it);
+                } else {
+                    ++it;
+                }
+            }
+        }
+
+        // ... then pass the request message and have the TA check it's signed by the
+        // key in last certificate we pushed.
+        if (sessionTranscript.size() > 0 && itemsRequest.size() > 0 && readerSignature.size() > 0) {
+            optional<vector<uint8_t>> tbsSignature = support::coseSignGetSignature(readerSignature);
+            if (!tbsSignature) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "Error extracting toBeSigned from COSE_Sign1"));
+            }
+            optional<int> coseSignAlg = support::coseSignGetAlg(readerSignature);
+            if (!coseSignAlg) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "Error extracting signature algorithm from COSE_Sign1"));
+            }
+            if (!hwProxy_->validateRequestMessage(sessionTranscript, itemsRequest,
+                                                  coseSignAlg.value(), tbsSignature.value())) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_READER_SIGNATURE_CHECK_FAILED,
+                        "readerMessage is not signed by top-level certificate"));
+            }
+        }
+    }
+
+    // Feed remaining access control profiles...
+    for (const SecureAccessControlProfile& profile : remainingAcps) {
+        optional<bool> res = hwProxy_->validateAccessControlProfile(
+                profile.id, profile.readerCertificate.encodedCertificate,
+                profile.userAuthenticationRequired, profile.timeoutMillis, profile.secureUserId,
+                profile.mac);
+        if (!res) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Error validating access control profile"));
+        }
+        if (res.value()) {
+            accessControlProfileMask |= (1 << profile.id);
+        }
+    }
+
+    // TODO: move this check to the TA
+#if 1
+    // To prevent replay-attacks, we check that the public part of the ephemeral
+    // key we previously created, is present in the DeviceEngagement part of
+    // SessionTranscript as a COSE_Key, in uncompressed form.
+    //
+    // We do this by just searching for the X and Y coordinates.
+    if (sessionTranscript.size() > 0) {
+        auto [getXYSuccess, ePubX, ePubY] = support::ecPublicKeyGetXandY(ephemeralPublicKey_);
+        if (!getXYSuccess) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                    "Error extracting X and Y from ePub"));
+        }
+        if (sessionTranscript.size() > 0 &&
+            !(memmem(sessionTranscript.data(), sessionTranscript.size(), ePubX.data(),
+                     ePubX.size()) != nullptr &&
+              memmem(sessionTranscript.data(), sessionTranscript.size(), ePubY.data(),
+                     ePubY.size()) != nullptr)) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_EPHEMERAL_PUBLIC_KEY_NOT_FOUND,
+                    "Did not find ephemeral public key's X and Y coordinates in "
+                    "SessionTranscript (make sure leading zeroes are not used)"));
+        }
+    }
+#endif
+
+    // itemsRequest: If non-empty, contains request data that may be signed by the
+    // reader.  The content can be defined in the way appropriate for the
+    // credential, but there are three requirements that must be met to work with
+    // this HAL:
+    if (itemsRequest.size() > 0) {
+        // 1. The content must be a CBOR-encoded structure.
+        auto [item, _, message] = cppbor::parse(itemsRequest);
+        if (item == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+                    "Error decoding CBOR in itemsRequest"));
+        }
+
+        // 2. The CBOR structure must be a map.
+        const cppbor::Map* map = item->asMap();
+        if (map == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+                    "itemsRequest is not a CBOR map"));
+        }
+
+        // 3. The map must contain a key "nameSpaces" whose value contains a map, as described in
+        //    the example below.
+        //
+        //   NameSpaces = {
+        //     + NameSpace => DataElements ; Requested data elements for each NameSpace
+        //   }
+        //
+        //   NameSpace = tstr
+        //
+        //   DataElements = {
+        //     + DataElement => IntentToRetain
+        //   }
+        //
+        //   DataElement = tstr
+        //   IntentToRetain = bool
+        //
+        // Here's an example of an |itemsRequest| CBOR value satisfying above requirements 1.
+        // through 3.:
+        //
+        //    {
+        //        'docType' : 'org.iso.18013-5.2019',
+        //        'nameSpaces' : {
+        //            'org.iso.18013-5.2019' : {
+        //                'Last name' : false,
+        //                'Birth date' : false,
+        //                'First name' : false,
+        //                'Home address' : true
+        //            },
+        //            'org.aamva.iso.18013-5.2019' : {
+        //                'Real Id' : false
+        //            }
+        //        }
+        //    }
+        //
+        const cppbor::Map* nsMap = nullptr;
+        for (size_t n = 0; n < map->size(); n++) {
+            const auto& [keyItem, valueItem] = (*map)[n];
+            if (keyItem->type() == cppbor::TSTR && keyItem->asTstr()->value() == "nameSpaces" &&
+                valueItem->type() == cppbor::MAP) {
+                nsMap = valueItem->asMap();
+                break;
+            }
+        }
+        if (nsMap == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+                    "No nameSpaces map in top-most map"));
+        }
+
+        for (size_t n = 0; n < nsMap->size(); n++) {
+            auto& [nsKeyItem, nsValueItem] = (*nsMap)[n];
+            const cppbor::Tstr* nsKey = nsKeyItem->asTstr();
+            const cppbor::Map* nsInnerMap = nsValueItem->asMap();
+            if (nsKey == nullptr || nsInnerMap == nullptr) {
+                return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                        IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+                        "Type mismatch in nameSpaces map"));
+            }
+            string requestedNamespace = nsKey->value();
+            set<string> requestedKeys;
+            for (size_t m = 0; m < nsInnerMap->size(); m++) {
+                const auto& [innerMapKeyItem, innerMapValueItem] = (*nsInnerMap)[m];
+                const cppbor::Tstr* nameItem = innerMapKeyItem->asTstr();
+                const cppbor::Simple* simple = innerMapValueItem->asSimple();
+                const cppbor::Bool* intentToRetainItem =
+                        (simple != nullptr) ? simple->asBool() : nullptr;
+                if (nameItem == nullptr || intentToRetainItem == nullptr) {
+                    return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                            IIdentityCredentialStore::STATUS_INVALID_ITEMS_REQUEST_MESSAGE,
+                            "Type mismatch in value in nameSpaces map"));
+                }
+                requestedKeys.insert(nameItem->value());
+            }
+            requestedNameSpacesAndNames_[requestedNamespace] = requestedKeys;
+        }
+    }
+
+    deviceNameSpacesMap_ = cppbor::Map();
+    currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+    requestCountsRemaining_ = requestCounts;
+    currentNameSpace_ = "";
+
+    itemsRequest_ = itemsRequest;
+    signingKeyBlob_ = signingKeyBlob;
+
+    // calculate the size of DeviceNameSpaces. We need to know it ahead of time.
+    calcDeviceNameSpacesSize(accessControlProfileMask);
+
+    // Count the number of non-empty namespaces
+    size_t numNamespacesWithValues = 0;
+    for (size_t n = 0; n < expectedNumEntriesPerNamespace_.size(); n++) {
+        if (expectedNumEntriesPerNamespace_[n] > 0) {
+            numNamespacesWithValues += 1;
+        }
+    }
+
+    // Finally, pass info so the HMAC key can be derived and the TA can start
+    // creating the DeviceNameSpaces CBOR...
+    if (sessionTranscript_.size() > 0 && readerPublicKey_.size() > 0 && signingKeyBlob.size() > 0) {
+        // We expect the reader ephemeral public key to be same size and curve
+        // as the ephemeral key we generated (e.g. P-256 key), otherwise ECDH
+        // won't work. So its length should be 65 bytes and it should be
+        // starting with 0x04.
+        if (readerPublicKey_.size() != 65 || readerPublicKey_[0] != 0x04) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED,
+                    "Reader public key is not in expected format"));
+        }
+        vector<uint8_t> pubKeyP256(readerPublicKey_.begin() + 1, readerPublicKey_.end());
+        if (!hwProxy_->calcMacKey(sessionTranscript_, pubKeyP256, signingKeyBlob, docType_,
+                                  numNamespacesWithValues, expectedDeviceNameSpacesSize_)) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED, "Error starting retrieving entries"));
+        }
+    }
+
+    numStartRetrievalCalls_ += 1;
+    return ndk::ScopedAStatus::ok();
+}
+
+size_t cborNumBytesForLength(size_t length) {
+    if (length < 24) {
+        return 0;
+    } else if (length <= 0xff) {
+        return 1;
+    } else if (length <= 0xffff) {
+        return 2;
+    } else if (length <= 0xffffffff) {
+        return 4;
+    }
+    return 8;
+}
+
+size_t cborNumBytesForTstr(const string& value) {
+    return 1 + cborNumBytesForLength(value.size()) + value.size();
+}
+
+void IdentityCredential::calcDeviceNameSpacesSize(uint32_t accessControlProfileMask) {
+    /*
+     * This is how DeviceNameSpaces is defined:
+     *
+     *        DeviceNameSpaces = {
+     *            * NameSpace => DeviceSignedItems
+     *        }
+     *        DeviceSignedItems = {
+     *            + DataItemName => DataItemValue
+     *        }
+     *
+     *        Namespace = tstr
+     *        DataItemName = tstr
+     *        DataItemValue = any
+     *
+     * This function will calculate its length using knowledge of how CBOR is
+     * encoded.
+     */
+    size_t ret = 0;
+    vector<unsigned int> numEntriesPerNamespace;
+    for (const RequestNamespace& rns : requestNamespaces_) {
+        vector<RequestDataItem> itemsToInclude;
+
+        for (const RequestDataItem& rdi : rns.items) {
+            // If we have a CBOR request message, skip if item isn't in it
+            if (itemsRequest_.size() > 0) {
+                const auto& it = requestedNameSpacesAndNames_.find(rns.namespaceName);
+                if (it == requestedNameSpacesAndNames_.end()) {
+                    continue;
+                }
+                const set<string>& dataItemNames = it->second;
+                if (dataItemNames.find(rdi.name) == dataItemNames.end()) {
+                    continue;
+                }
+            }
+
+            // Access is granted if at least one of the profiles grants access.
+            //
+            // If an item is configured without any profiles, access is denied.
+            //
+            bool authorized = false;
+            for (auto id : rdi.accessControlProfileIds) {
+                if (accessControlProfileMask & (1 << id)) {
+                    authorized = true;
+                    break;
+                }
+            }
+            if (!authorized) {
+                continue;
+            }
+
+            itemsToInclude.push_back(rdi);
+        }
+
+        numEntriesPerNamespace.push_back(itemsToInclude.size());
+
+        // If no entries are to be in the namespace, we don't include it in
+        // the CBOR...
+        if (itemsToInclude.size() == 0) {
+            continue;
+        }
+
+        // Key: NameSpace
+        ret += cborNumBytesForTstr(rns.namespaceName);
+
+        // Value: Open the DeviceSignedItems map
+        ret += 1 + cborNumBytesForLength(itemsToInclude.size());
+
+        for (const RequestDataItem& item : itemsToInclude) {
+            // Key: DataItemName
+            ret += cborNumBytesForTstr(item.name);
+
+            // Value: DataItemValue - entryData.size is the length of serialized CBOR so we use
+            // that.
+            ret += item.size;
+        }
+    }
+
+    // Now that we know the number of namespaces with values, we know how many
+    // bytes the DeviceNamespaces map in the beginning is going to take up.
+    ret += 1 + cborNumBytesForLength(numEntriesPerNamespace.size());
+
+    expectedDeviceNameSpacesSize_ = ret;
+    expectedNumEntriesPerNamespace_ = numEntriesPerNamespace;
+}
+
+ndk::ScopedAStatus IdentityCredential::startRetrieveEntryValue(
+        const string& nameSpace, const string& name, int32_t entrySize,
+        const vector<int32_t>& accessControlProfileIds) {
+    if (name.empty()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Name cannot be empty"));
+    }
+    if (nameSpace.empty()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Name space cannot be empty"));
+    }
+
+    if (requestCountsRemaining_.size() == 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "No more name spaces left to go through"));
+    }
+
+    bool newNamespace;
+    if (currentNameSpace_ == "") {
+        // First call.
+        currentNameSpace_ = nameSpace;
+        newNamespace = true;
+    }
+
+    if (nameSpace == currentNameSpace_) {
+        // Same namespace.
+        if (requestCountsRemaining_[0] == 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "No more entries to be retrieved in current name space"));
+        }
+        requestCountsRemaining_[0] -= 1;
+    } else {
+        // New namespace.
+        if (requestCountsRemaining_[0] != 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Moved to new name space but one or more entries need to be retrieved "
+                    "in current name space"));
+        }
+        if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+            deviceNameSpacesMap_.add(currentNameSpace_,
+                                     std::move(currentNameSpaceDeviceNameSpacesMap_));
+        }
+        currentNameSpaceDeviceNameSpacesMap_ = cppbor::Map();
+
+        requestCountsRemaining_.erase(requestCountsRemaining_.begin());
+        currentNameSpace_ = nameSpace;
+        newNamespace = true;
+    }
+
+    // It's permissible to have an empty itemsRequest... but if non-empty you can
+    // only request what was specified in said itemsRequest. Enforce that.
+    if (itemsRequest_.size() > 0) {
+        const auto& it = requestedNameSpacesAndNames_.find(nameSpace);
+        if (it == requestedNameSpacesAndNames_.end()) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+                    "Name space was not requested in startRetrieval"));
+        }
+        const set<string>& dataItemNames = it->second;
+        if (dataItemNames.find(name) == dataItemNames.end()) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_NOT_IN_REQUEST_MESSAGE,
+                    "Data item name in name space was not requested in startRetrieval"));
+        }
+    }
+
+    unsigned int newNamespaceNumEntries = 0;
+    if (newNamespace) {
+        if (expectedNumEntriesPerNamespace_.size() == 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "No more populated name spaces left to go through"));
+        }
+        newNamespaceNumEntries = expectedNumEntriesPerNamespace_[0];
+        expectedNumEntriesPerNamespace_.erase(expectedNumEntriesPerNamespace_.begin());
+    }
+
+    // Access control is enforced in the secure hardware.
+    //
+    // ... except for STATUS_NOT_IN_REQUEST_MESSAGE, that's handled above (TODO:
+    // consolidate).
+    //
+    AccessCheckResult res = hwProxy_->startRetrieveEntryValue(
+            nameSpace, name, newNamespaceNumEntries, entrySize, accessControlProfileIds);
+    switch (res) {
+        case AccessCheckResult::kOk:
+            /* Do nothing. */
+            break;
+        case AccessCheckResult::kFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_FAILED,
+                    "Access control check failed (failed)"));
+            break;
+        case AccessCheckResult::kNoAccessControlProfiles:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_NO_ACCESS_CONTROL_PROFILES,
+                    "Access control check failed (no access control profiles)"));
+            break;
+        case AccessCheckResult::kUserAuthenticationFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_USER_AUTHENTICATION_FAILED,
+                    "Access control check failed (user auth)"));
+            break;
+        case AccessCheckResult::kReaderAuthenticationFailed:
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_READER_AUTHENTICATION_FAILED,
+                    "Access control check failed (reader auth)"));
+            break;
+    }
+
+    currentName_ = name;
+    currentAccessControlProfileIds_ = accessControlProfileIds;
+    entryRemainingBytes_ = entrySize;
+    entryValue_.resize(0);
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::retrieveEntryValue(const vector<uint8_t>& encryptedContent,
+                                                          vector<uint8_t>* outContent) {
+    optional<vector<uint8_t>> content = hwProxy_->retrieveEntryValue(
+            encryptedContent, currentNameSpace_, currentName_, currentAccessControlProfileIds_);
+    if (!content) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Error decrypting data"));
+    }
+
+    size_t chunkSize = content.value().size();
+
+    if (chunkSize > entryRemainingBytes_) {
+        LOG(ERROR) << "Retrieved chunk of size " << chunkSize
+                   << " is bigger than remaining space of size " << entryRemainingBytes_;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Retrieved chunk is bigger than remaining space"));
+    }
+
+    entryRemainingBytes_ -= chunkSize;
+    if (entryRemainingBytes_ > 0) {
+        if (chunkSize != IdentityCredentialStore::kGcmChunkSize) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Retrieved non-final chunk of size which isn't kGcmChunkSize"));
+        }
+    }
+
+    entryValue_.insert(entryValue_.end(), content.value().begin(), content.value().end());
+
+    if (entryRemainingBytes_ == 0) {
+        auto [entryValueItem, _, message] = cppbor::parse(entryValue_);
+        if (entryValueItem == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Retrieved data which is invalid CBOR"));
+        }
+        currentNameSpaceDeviceNameSpacesMap_.add(currentName_, std::move(entryValueItem));
+    }
+
+    *outContent = content.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::finishRetrieval(vector<uint8_t>* outMac,
+                                                       vector<uint8_t>* outDeviceNameSpaces) {
+    if (currentNameSpaceDeviceNameSpacesMap_.size() > 0) {
+        deviceNameSpacesMap_.add(currentNameSpace_,
+                                 std::move(currentNameSpaceDeviceNameSpacesMap_));
+    }
+    vector<uint8_t> encodedDeviceNameSpaces = deviceNameSpacesMap_.encode();
+
+    if (encodedDeviceNameSpaces.size() != expectedDeviceNameSpacesSize_) {
+        LOG(ERROR) << "encodedDeviceNameSpaces is " << encodedDeviceNameSpaces.size() << " bytes, "
+                   << "was expecting " << expectedDeviceNameSpacesSize_;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                StringPrintf(
+                        "Unexpected CBOR size %zd for encodedDeviceNameSpaces, was expecting %zd",
+                        encodedDeviceNameSpaces.size(), expectedDeviceNameSpacesSize_)
+                        .c_str()));
+    }
+
+    // If there's no signing key or no sessionTranscript or no reader ephemeral
+    // public key, we return the empty MAC.
+    optional<vector<uint8_t>> mac;
+    if (signingKeyBlob_.size() > 0 && sessionTranscript_.size() > 0 &&
+        readerPublicKey_.size() > 0) {
+        optional<vector<uint8_t>> digestToBeMaced = hwProxy_->finishRetrieval();
+        if (!digestToBeMaced || digestToBeMaced.value().size() != 32) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Error generating digestToBeMaced"));
+        }
+        // Now construct COSE_Mac0 from the returned MAC...
+        mac = support::coseMacWithDigest(digestToBeMaced.value(), {} /* data */);
+    }
+
+    *outMac = mac.value_or(vector<uint8_t>({}));
+    *outDeviceNameSpaces = encodedDeviceNameSpaces;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::generateSigningKeyPair(
+        vector<uint8_t>* outSigningKeyBlob, Certificate* outSigningKeyCertificate) {
+    time_t now = time(NULL);
+    optional<pair<vector<uint8_t>, vector<uint8_t>>> pair =
+            hwProxy_->generateSigningKeyPair(docType_, now);
+    if (!pair) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error creating signingKey"));
+    }
+
+    *outSigningKeyCertificate = Certificate();
+    outSigningKeyCertificate->encodedCertificate = pair->first;
+
+    *outSigningKeyBlob = pair->second;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredential::updateCredential(
+        shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+    sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
+    shared_ptr<WritableIdentityCredential> wc =
+            ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType_,
+                                                                 testCredential_);
+    if (!wc->initializeForUpdate(encryptedCredentialKeys_)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error initializing WritableIdentityCredential for update"));
+    }
+    *outWritableCredential = wc;
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/IdentityCredential.h b/identity/aidl/default/common/IdentityCredential.h
new file mode 100644
index 0000000..ef9d133
--- /dev/null
+++ b/identity/aidl/default/common/IdentityCredential.h
@@ -0,0 +1,145 @@
+/*
+ * Copyright 2019, 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_IDENTITY_IDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredential.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <vector>
+
+#include <cppbor.h>
+
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::aidl::android::hardware::keymaster::HardwareAuthToken;
+using ::aidl::android::hardware::keymaster::VerificationToken;
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwarePresentationProxy;
+using ::std::map;
+using ::std::set;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredential : public BnIdentityCredential {
+  public:
+    IdentityCredential(sp<SecureHardwareProxyFactory> hwProxyFactory,
+                       sp<SecureHardwarePresentationProxy> hwProxy,
+                       const vector<uint8_t>& credentialData)
+        : hwProxyFactory_(hwProxyFactory),
+          hwProxy_(hwProxy),
+          credentialData_(credentialData),
+          numStartRetrievalCalls_(0),
+          expectedDeviceNameSpacesSize_(0) {}
+
+    // Parses and decrypts credentialData_, return a status code from
+    // IIdentityCredentialStore. Must be called right after construction.
+    int initialize();
+
+    // Methods from IIdentityCredential follow.
+    ndk::ScopedAStatus deleteCredential(vector<uint8_t>* outProofOfDeletionSignature) override;
+    ndk::ScopedAStatus deleteCredentialWithChallenge(
+            const vector<uint8_t>& challenge,
+            vector<uint8_t>* outProofOfDeletionSignature) override;
+    ndk::ScopedAStatus proveOwnership(const vector<uint8_t>& challenge,
+                                      vector<uint8_t>* outProofOfOwnershipSignature) override;
+    ndk::ScopedAStatus createEphemeralKeyPair(vector<uint8_t>* outKeyPair) override;
+    ndk::ScopedAStatus setReaderEphemeralPublicKey(const vector<uint8_t>& publicKey) override;
+    ndk::ScopedAStatus createAuthChallenge(int64_t* outChallenge) override;
+    ndk::ScopedAStatus setRequestedNamespaces(
+            const vector<RequestNamespace>& requestNamespaces) override;
+    ndk::ScopedAStatus setVerificationToken(const VerificationToken& verificationToken) override;
+    ndk::ScopedAStatus startRetrieval(
+            const vector<SecureAccessControlProfile>& accessControlProfiles,
+            const HardwareAuthToken& authToken, const vector<uint8_t>& itemsRequest,
+            const vector<uint8_t>& signingKeyBlob, const vector<uint8_t>& sessionTranscript,
+            const vector<uint8_t>& readerSignature, const vector<int32_t>& requestCounts) override;
+    ndk::ScopedAStatus startRetrieveEntryValue(
+            const string& nameSpace, const string& name, int32_t entrySize,
+            const vector<int32_t>& accessControlProfileIds) override;
+    ndk::ScopedAStatus retrieveEntryValue(const vector<uint8_t>& encryptedContent,
+                                          vector<uint8_t>* outContent) override;
+    ndk::ScopedAStatus finishRetrieval(vector<uint8_t>* outMac,
+                                       vector<uint8_t>* outDeviceNameSpaces) override;
+    ndk::ScopedAStatus generateSigningKeyPair(vector<uint8_t>* outSigningKeyBlob,
+                                              Certificate* outSigningKeyCertificate) override;
+
+    ndk::ScopedAStatus updateCredential(
+            shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+  private:
+    ndk::ScopedAStatus deleteCredentialCommon(const vector<uint8_t>& challenge,
+                                              bool includeChallenge,
+                                              vector<uint8_t>* outProofOfDeletionSignature);
+
+    // Set by constructor
+    sp<SecureHardwareProxyFactory> hwProxyFactory_;
+    sp<SecureHardwarePresentationProxy> hwProxy_;
+    vector<uint8_t> credentialData_;
+    int numStartRetrievalCalls_;
+
+    // Set by initialize()
+    string docType_;
+    bool testCredential_;
+    vector<uint8_t> encryptedCredentialKeys_;
+
+    // Set by createEphemeralKeyPair()
+    vector<uint8_t> ephemeralPublicKey_;
+
+    // Set by setReaderEphemeralPublicKey()
+    vector<uint8_t> readerPublicKey_;
+
+    // Set by setRequestedNamespaces()
+    vector<RequestNamespace> requestNamespaces_;
+
+    // Set by setVerificationToken().
+    VerificationToken verificationToken_;
+
+    // Set at startRetrieval() time.
+    vector<uint8_t> signingKeyBlob_;
+    vector<uint8_t> sessionTranscript_;
+    vector<uint8_t> itemsRequest_;
+    vector<int32_t> requestCountsRemaining_;
+    map<string, set<string>> requestedNameSpacesAndNames_;
+    cppbor::Map deviceNameSpacesMap_;
+    cppbor::Map currentNameSpaceDeviceNameSpacesMap_;
+
+    // Calculated at startRetrieval() time.
+    size_t expectedDeviceNameSpacesSize_;
+    vector<unsigned int> expectedNumEntriesPerNamespace_;
+
+    // Set at startRetrieveEntryValue() time.
+    string currentNameSpace_;
+    string currentName_;
+    vector<int32_t> currentAccessControlProfileIds_;
+    size_t entryRemainingBytes_;
+    vector<uint8_t> entryValue_;
+
+    void calcDeviceNameSpacesSize(uint32_t accessControlProfileMask);
+};
+
+}  // namespace aidl::android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/common/IdentityCredentialStore.cpp b/identity/aidl/default/common/IdentityCredentialStore.cpp
new file mode 100644
index 0000000..e6b5466
--- /dev/null
+++ b/identity/aidl/default/common/IdentityCredentialStore.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#define LOG_TAG "IdentityCredentialStore"
+
+#include <android-base/logging.h>
+
+#include "IdentityCredential.h"
+#include "IdentityCredentialStore.h"
+#include "WritableIdentityCredential.h"
+
+namespace aidl::android::hardware::identity {
+
+ndk::ScopedAStatus IdentityCredentialStore::getHardwareInformation(
+        HardwareInformation* hardwareInformation) {
+    HardwareInformation hw;
+    hw.credentialStoreName = "Identity Credential Reference Implementation";
+    hw.credentialStoreAuthorName = "Google";
+    hw.dataChunkSize = kGcmChunkSize;
+    hw.isDirectAccess = false;
+    hw.supportedDocTypes = {};
+    *hardwareInformation = hw;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::createCredential(
+        const string& docType, bool testCredential,
+        shared_ptr<IWritableIdentityCredential>* outWritableCredential) {
+    sp<SecureHardwareProvisioningProxy> hwProxy = hwProxyFactory_->createProvisioningProxy();
+    shared_ptr<WritableIdentityCredential> wc =
+            ndk::SharedRefBase::make<WritableIdentityCredential>(hwProxy, docType, testCredential);
+    if (!wc->initialize()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error initializing WritableIdentityCredential"));
+    }
+    *outWritableCredential = wc;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus IdentityCredentialStore::getCredential(
+        CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
+        shared_ptr<IIdentityCredential>* outCredential) {
+    // We only support CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256 right now.
+    if (cipherSuite != CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_CIPHER_SUITE_NOT_SUPPORTED,
+                "Unsupported cipher suite"));
+    }
+
+    sp<SecureHardwarePresentationProxy> hwProxy = hwProxyFactory_->createPresentationProxy();
+    shared_ptr<IdentityCredential> credential =
+            ndk::SharedRefBase::make<IdentityCredential>(hwProxyFactory_, hwProxy, credentialData);
+    auto ret = credential->initialize();
+    if (ret != IIdentityCredentialStore::STATUS_OK) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                int(ret), "Error initializing IdentityCredential"));
+    }
+    *outCredential = credential;
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/IdentityCredentialStore.h b/identity/aidl/default/common/IdentityCredentialStore.h
new file mode 100644
index 0000000..d35e632
--- /dev/null
+++ b/identity/aidl/default/common/IdentityCredentialStore.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2019, 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_IDENTITY_IDENTITYCREDENTIALSTORE_H
+#define ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
+
+#include <aidl/android/hardware/identity/BnIdentityCredentialStore.h>
+
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class IdentityCredentialStore : public BnIdentityCredentialStore {
+  public:
+    IdentityCredentialStore(sp<SecureHardwareProxyFactory> hwProxyFactory)
+        : hwProxyFactory_(hwProxyFactory) {}
+
+    // The GCM chunk size used by this implementation is 64 KiB.
+    static constexpr size_t kGcmChunkSize = 64 * 1024;
+
+    // Methods from IIdentityCredentialStore follow.
+    ndk::ScopedAStatus getHardwareInformation(HardwareInformation* hardwareInformation) override;
+
+    ndk::ScopedAStatus createCredential(
+            const string& docType, bool testCredential,
+            shared_ptr<IWritableIdentityCredential>* outWritableCredential) override;
+
+    ndk::ScopedAStatus getCredential(CipherSuite cipherSuite, const vector<uint8_t>& credentialData,
+                                     shared_ptr<IIdentityCredential>* outCredential) override;
+
+  private:
+    sp<SecureHardwareProxyFactory> hwProxyFactory_;
+};
+
+}  // namespace aidl::android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_IDENTITYCREDENTIALSTORE_H
diff --git a/identity/aidl/default/common/SecureHardwareProxy.h b/identity/aidl/default/common/SecureHardwareProxy.h
new file mode 100644
index 0000000..a1ed1ef
--- /dev/null
+++ b/identity/aidl/default/common/SecureHardwareProxy.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 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_IDENTITY_SECUREHARDWAREPROXY_H
+#define ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
+
+#include <utils/RefBase.h>
+#include <optional>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::identity {
+
+using ::android::RefBase;
+using ::std::optional;
+using ::std::pair;
+using ::std::string;
+using ::std::vector;
+
+// These classes are used to communicate with Secure Hardware. They mimic the
+// API in libEmbeddedIC 1:1 (except for using C++ types) as each call is intended
+// to be forwarded to the Secure Hardware.
+//
+// Instances are instantiated when a provisioning or presentation session
+// starts. When the session is complete, the shutdown() method is called.
+//
+
+// Forward declare.
+//
+class SecureHardwareProvisioningProxy;
+class SecureHardwarePresentationProxy;
+
+// This is a class used to create proxies.
+//
+class SecureHardwareProxyFactory : public RefBase {
+  public:
+    SecureHardwareProxyFactory() {}
+    virtual ~SecureHardwareProxyFactory() {}
+
+    virtual sp<SecureHardwareProvisioningProxy> createProvisioningProxy() = 0;
+    virtual sp<SecureHardwarePresentationProxy> createPresentationProxy() = 0;
+};
+
+// The proxy used for provisioning.
+//
+class SecureHardwareProvisioningProxy : public RefBase {
+  public:
+    SecureHardwareProvisioningProxy() {}
+    virtual ~SecureHardwareProvisioningProxy() {}
+
+    virtual bool initialize(bool testCredential) = 0;
+
+    virtual bool initializeForUpdate(bool testCredential, string docType,
+                                     vector<uint8_t> encryptedCredentialKeys) = 0;
+
+    // Returns public key certificate chain with attestation.
+    //
+    // This must return an entire certificate chain and its implementation must
+    // be coordinated with the implementation of eicOpsCreateCredentialKey() on
+    // the TA side (which may return just a single certificate or the entire
+    // chain).
+    virtual optional<vector<uint8_t>> createCredentialKey(const vector<uint8_t>& challenge,
+                                                          const vector<uint8_t>& applicationId) = 0;
+
+    virtual bool startPersonalization(int accessControlProfileCount, vector<int> entryCounts,
+                                      const string& docType,
+                                      size_t expectedProofOfProvisioningSize) = 0;
+
+    // Returns MAC (28 bytes).
+    virtual optional<vector<uint8_t>> addAccessControlProfile(
+            int id, const vector<uint8_t>& readerCertificate, bool userAuthenticationRequired,
+            uint64_t timeoutMillis, uint64_t secureUserId) = 0;
+
+    virtual bool beginAddEntry(const vector<int>& accessControlProfileIds, const string& nameSpace,
+                               const string& name, uint64_t entrySize) = 0;
+
+    // Returns encryptedContent.
+    virtual optional<vector<uint8_t>> addEntryValue(const vector<int>& accessControlProfileIds,
+                                                    const string& nameSpace, const string& name,
+                                                    const vector<uint8_t>& content) = 0;
+
+    // Returns signatureOfToBeSigned (EIC_ECDSA_P256_SIGNATURE_SIZE bytes).
+    virtual optional<vector<uint8_t>> finishAddingEntries() = 0;
+
+    // Returns encryptedCredentialKeys (80 bytes).
+    virtual optional<vector<uint8_t>> finishGetCredentialData(const string& docType) = 0;
+
+    virtual bool shutdown() = 0;
+};
+
+enum AccessCheckResult {
+    kOk,
+    kFailed,
+    kNoAccessControlProfiles,
+    kUserAuthenticationFailed,
+    kReaderAuthenticationFailed,
+};
+
+// The proxy used for presentation.
+//
+class SecureHardwarePresentationProxy : public RefBase {
+  public:
+    SecureHardwarePresentationProxy() {}
+    virtual ~SecureHardwarePresentationProxy() {}
+
+    virtual bool initialize(bool testCredential, string docType,
+                            vector<uint8_t> encryptedCredentialKeys) = 0;
+
+    // Returns publicKeyCert (1st component) and signingKeyBlob (2nd component)
+    virtual optional<pair<vector<uint8_t>, vector<uint8_t>>> generateSigningKeyPair(string docType,
+                                                                                    time_t now) = 0;
+
+    // Returns private key
+    virtual optional<vector<uint8_t>> createEphemeralKeyPair() = 0;
+
+    virtual optional<uint64_t> createAuthChallenge() = 0;
+
+    virtual bool startRetrieveEntries() = 0;
+
+    virtual bool setAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                              int hardwareAuthenticatorType, uint64_t timeStamp,
+                              const vector<uint8_t>& mac, uint64_t verificationTokenChallenge,
+                              uint64_t verificationTokenTimestamp,
+                              int verificationTokenSecurityLevel,
+                              const vector<uint8_t>& verificationTokenMac) = 0;
+
+    virtual bool pushReaderCert(const vector<uint8_t>& certX509) = 0;
+
+    virtual optional<bool> validateAccessControlProfile(int id,
+                                                        const vector<uint8_t>& readerCertificate,
+                                                        bool userAuthenticationRequired,
+                                                        int timeoutMillis, uint64_t secureUserId,
+                                                        const vector<uint8_t>& mac) = 0;
+
+    virtual bool validateRequestMessage(const vector<uint8_t>& sessionTranscript,
+                                        const vector<uint8_t>& requestMessage, int coseSignAlg,
+                                        const vector<uint8_t>& readerSignatureOfToBeSigned) = 0;
+
+    virtual bool calcMacKey(const vector<uint8_t>& sessionTranscript,
+                            const vector<uint8_t>& readerEphemeralPublicKey,
+                            const vector<uint8_t>& signingKeyBlob, const string& docType,
+                            unsigned int numNamespacesWithValues,
+                            size_t expectedProofOfProvisioningSize) = 0;
+
+    virtual AccessCheckResult startRetrieveEntryValue(
+            const string& nameSpace, const string& name, unsigned int newNamespaceNumEntries,
+            int32_t entrySize, const vector<int32_t>& accessControlProfileIds) = 0;
+
+    virtual optional<vector<uint8_t>> retrieveEntryValue(
+            const vector<uint8_t>& encryptedContent, const string& nameSpace, const string& name,
+            const vector<int32_t>& accessControlProfileIds) = 0;
+
+    virtual optional<vector<uint8_t>> finishRetrieval();
+
+    virtual optional<vector<uint8_t>> deleteCredential(const string& docType,
+                                                       const vector<uint8_t>& challenge,
+                                                       bool includeChallenge,
+                                                       size_t proofOfDeletionCborSize) = 0;
+
+    virtual optional<vector<uint8_t>> proveOwnership(const string& docType, bool testCredential,
+                                                     const vector<uint8_t>& challenge,
+                                                     size_t proofOfOwnershipCborSize) = 0;
+
+    virtual bool shutdown() = 0;
+};
+
+}  // namespace android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_SECUREHARDWAREPROXY_H
diff --git a/identity/aidl/default/common/WritableIdentityCredential.cpp b/identity/aidl/default/common/WritableIdentityCredential.cpp
new file mode 100644
index 0000000..200ee61
--- /dev/null
+++ b/identity/aidl/default/common/WritableIdentityCredential.cpp
@@ -0,0 +1,405 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#define LOG_TAG "WritableIdentityCredential"
+
+#include "WritableIdentityCredential.h"
+
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <android-base/logging.h>
+#include <android-base/stringprintf.h>
+
+#include <cppbor.h>
+#include <cppbor_parse.h>
+
+#include <utility>
+
+#include "IdentityCredentialStore.h"
+
+#include "FakeSecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::base::StringPrintf;
+using ::std::optional;
+using namespace ::android::hardware::identity;
+
+bool WritableIdentityCredential::initialize() {
+    if (!hwProxy_->initialize(testCredential_)) {
+        LOG(ERROR) << "hwProxy->initialize() failed";
+        return false;
+    }
+    startPersonalizationCalled_ = false;
+    firstEntry_ = true;
+
+    return true;
+}
+
+// Used when updating a credential. Returns false on failure.
+bool WritableIdentityCredential::initializeForUpdate(
+        const vector<uint8_t>& encryptedCredentialKeys) {
+    if (!hwProxy_->initializeForUpdate(testCredential_, docType_, encryptedCredentialKeys)) {
+        LOG(ERROR) << "hwProxy->initializeForUpdate() failed";
+        return false;
+    }
+    startPersonalizationCalled_ = false;
+    firstEntry_ = true;
+
+    return true;
+}
+
+WritableIdentityCredential::~WritableIdentityCredential() {}
+
+ndk::ScopedAStatus WritableIdentityCredential::getAttestationCertificate(
+        const vector<uint8_t>& attestationApplicationId,
+        const vector<uint8_t>& attestationChallenge, vector<Certificate>* outCertificateChain) {
+    if (getAttestationCertificateAlreadyCalled_) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error attestation certificate previously generated"));
+    }
+    getAttestationCertificateAlreadyCalled_ = true;
+
+    if (attestationChallenge.empty()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "Challenge can not be empty"));
+    }
+
+    optional<vector<uint8_t>> certChain =
+            hwProxy_->createCredentialKey(attestationChallenge, attestationApplicationId);
+    if (!certChain) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error generating attestation certificate chain"));
+    }
+
+    optional<vector<vector<uint8_t>>> certs = support::certificateChainSplit(certChain.value());
+    if (!certs) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error splitting chain into separate certificates"));
+    }
+
+    *outCertificateChain = vector<Certificate>();
+    for (const vector<uint8_t>& cert : certs.value()) {
+        Certificate c = Certificate();
+        c.encodedCertificate = cert;
+        outCertificateChain->push_back(std::move(c));
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::setExpectedProofOfProvisioningSize(
+        int32_t expectedProofOfProvisioningSize) {
+    expectedProofOfProvisioningSize_ = expectedProofOfProvisioningSize;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::startPersonalization(
+        int32_t accessControlProfileCount, const vector<int32_t>& entryCounts) {
+    if (startPersonalizationCalled_) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "startPersonalization called already"));
+    }
+    startPersonalizationCalled_ = true;
+
+    numAccessControlProfileRemaining_ = accessControlProfileCount;
+    remainingEntryCounts_ = entryCounts;
+    entryNameSpace_ = "";
+
+    signedDataAccessControlProfiles_ = cppbor::Array();
+    signedDataNamespaces_ = cppbor::Map();
+    signedDataCurrentNamespace_ = cppbor::Array();
+
+    if (!hwProxy_->startPersonalization(accessControlProfileCount, entryCounts, docType_,
+                                        expectedProofOfProvisioningSize_)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicStartPersonalization"));
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addAccessControlProfile(
+        int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+        int64_t timeoutMillis, int64_t secureUserId,
+        SecureAccessControlProfile* outSecureAccessControlProfile) {
+    if (numAccessControlProfileRemaining_ == 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "numAccessControlProfileRemaining_ is 0 and expected non-zero"));
+    }
+
+    if (accessControlProfileIds_.find(id) != accessControlProfileIds_.end()) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Access Control Profile id must be unique"));
+    }
+    accessControlProfileIds_.insert(id);
+
+    if (id < 0 || id >= 32) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Access Control Profile id must be non-negative and less than 32"));
+    }
+
+    // Spec requires if |userAuthenticationRequired| is false, then |timeoutMillis| must also
+    // be zero.
+    if (!userAuthenticationRequired && timeoutMillis != 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "userAuthenticationRequired is false but timeout is non-zero"));
+    }
+
+    optional<vector<uint8_t>> mac = hwProxy_->addAccessControlProfile(
+            id, readerCertificate.encodedCertificate, userAuthenticationRequired, timeoutMillis,
+            secureUserId);
+    if (!mac) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicAddAccessControlProfile"));
+    }
+
+    SecureAccessControlProfile profile;
+    profile.id = id;
+    profile.readerCertificate = readerCertificate;
+    profile.userAuthenticationRequired = userAuthenticationRequired;
+    profile.timeoutMillis = timeoutMillis;
+    profile.secureUserId = secureUserId;
+    profile.mac = mac.value();
+    cppbor::Map profileMap;
+    profileMap.add("id", profile.id);
+    if (profile.readerCertificate.encodedCertificate.size() > 0) {
+        profileMap.add("readerCertificate",
+                       cppbor::Bstr(profile.readerCertificate.encodedCertificate));
+    }
+    if (profile.userAuthenticationRequired) {
+        profileMap.add("userAuthenticationRequired", profile.userAuthenticationRequired);
+        profileMap.add("timeoutMillis", profile.timeoutMillis);
+    }
+    signedDataAccessControlProfiles_.add(std::move(profileMap));
+
+    numAccessControlProfileRemaining_--;
+
+    *outSecureAccessControlProfile = profile;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::beginAddEntry(
+        const vector<int32_t>& accessControlProfileIds, const string& nameSpace, const string& name,
+        int32_t entrySize) {
+    if (numAccessControlProfileRemaining_ != 0) {
+        LOG(ERROR) << "numAccessControlProfileRemaining_ is " << numAccessControlProfileRemaining_
+                   << " and expected zero";
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "numAccessControlProfileRemaining_ is not zero"));
+    }
+
+    // Ensure passed-in profile ids reference valid access control profiles
+    for (const int32_t id : accessControlProfileIds) {
+        if (accessControlProfileIds_.find(id) == accessControlProfileIds_.end()) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "An id in accessControlProfileIds references non-existing ACP"));
+        }
+    }
+
+    if (remainingEntryCounts_.size() == 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA, "No more namespaces to add to"));
+    }
+
+    // Handle initial beginEntry() call.
+    if (firstEntry_) {
+        firstEntry_ = false;
+        entryNameSpace_ = nameSpace;
+        allNameSpaces_.insert(nameSpace);
+    }
+
+    // If the namespace changed...
+    if (nameSpace != entryNameSpace_) {
+        if (allNameSpaces_.find(nameSpace) != allNameSpaces_.end()) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Name space cannot be added in interleaving fashion"));
+        }
+
+        // Then check that all entries in the previous namespace have been added..
+        if (remainingEntryCounts_[0] != 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "New namespace but a non-zero number of entries remain to be added"));
+        }
+        remainingEntryCounts_.erase(remainingEntryCounts_.begin());
+        remainingEntryCounts_[0] -= 1;
+        allNameSpaces_.insert(nameSpace);
+
+        if (signedDataCurrentNamespace_.size() > 0) {
+            signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+            signedDataCurrentNamespace_ = cppbor::Array();
+        }
+    } else {
+        // Same namespace...
+        if (remainingEntryCounts_[0] == 0) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Same namespace but no entries remain to be added"));
+        }
+        remainingEntryCounts_[0] -= 1;
+    }
+
+    entryRemainingBytes_ = entrySize;
+    entryNameSpace_ = nameSpace;
+    entryName_ = name;
+    entryAccessControlProfileIds_ = accessControlProfileIds;
+    entryBytes_.resize(0);
+    // LOG(INFO) << "name=" << name << " entrySize=" << entrySize;
+
+    if (!hwProxy_->beginAddEntry(accessControlProfileIds, nameSpace, name, entrySize)) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicBeginAddEntry"));
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::addEntryValue(const vector<uint8_t>& content,
+                                                             vector<uint8_t>* outEncryptedContent) {
+    size_t contentSize = content.size();
+
+    if (contentSize > IdentityCredentialStore::kGcmChunkSize) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Passed in chunk of is bigger than kGcmChunkSize"));
+    }
+    if (contentSize > entryRemainingBytes_) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "Passed in chunk is bigger than remaining space"));
+    }
+
+    entryBytes_.insert(entryBytes_.end(), content.begin(), content.end());
+    entryRemainingBytes_ -= contentSize;
+    if (entryRemainingBytes_ > 0) {
+        if (contentSize != IdentityCredentialStore::kGcmChunkSize) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA,
+                    "Retrieved non-final chunk which isn't kGcmChunkSize"));
+        }
+    }
+
+    optional<vector<uint8_t>> encryptedContent = hwProxy_->addEntryValue(
+            entryAccessControlProfileIds_, entryNameSpace_, entryName_, content);
+    if (!encryptedContent) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicAddEntryValue"));
+    }
+
+    if (entryRemainingBytes_ == 0) {
+        // TODO: ideally do do this without parsing the data (but still validate data is valid
+        // CBOR).
+        auto [item, _, message] = cppbor::parse(entryBytes_);
+        if (item == nullptr) {
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                    IIdentityCredentialStore::STATUS_INVALID_DATA, "Data is not valid CBOR"));
+        }
+        cppbor::Map entryMap;
+        entryMap.add("name", entryName_);
+        entryMap.add("value", std::move(item));
+        cppbor::Array profileIdArray;
+        for (auto id : entryAccessControlProfileIds_) {
+            profileIdArray.add(id);
+        }
+        entryMap.add("accessControlProfiles", std::move(profileIdArray));
+        signedDataCurrentNamespace_.add(std::move(entryMap));
+    }
+
+    *outEncryptedContent = encryptedContent.value();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus WritableIdentityCredential::finishAddingEntries(
+        vector<uint8_t>* outCredentialData, vector<uint8_t>* outProofOfProvisioningSignature) {
+    if (numAccessControlProfileRemaining_ != 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "numAccessControlProfileRemaining_ is not 0 and expected zero"));
+    }
+
+    if (remainingEntryCounts_.size() > 1 || remainingEntryCounts_[0] != 0) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                "More entry spaces remain than startPersonalization configured"));
+    }
+
+    if (signedDataCurrentNamespace_.size() > 0) {
+        signedDataNamespaces_.add(entryNameSpace_, std::move(signedDataCurrentNamespace_));
+    }
+    cppbor::Array popArray;
+    popArray.add("ProofOfProvisioning")
+            .add(docType_)
+            .add(std::move(signedDataAccessControlProfiles_))
+            .add(std::move(signedDataNamespaces_))
+            .add(testCredential_);
+    vector<uint8_t> encodedCbor = popArray.encode();
+
+    if (encodedCbor.size() != expectedProofOfProvisioningSize_) {
+        LOG(ERROR) << "CBOR for proofOfProvisioning is " << encodedCbor.size() << " bytes, "
+                   << "was expecting " << expectedProofOfProvisioningSize_;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_INVALID_DATA,
+                StringPrintf("Unexpected CBOR size %zd for proofOfProvisioning, was expecting %zd",
+                             encodedCbor.size(), expectedProofOfProvisioningSize_)
+                        .c_str()));
+    }
+
+    optional<vector<uint8_t>> signatureOfToBeSigned = hwProxy_->finishAddingEntries();
+    if (!signatureOfToBeSigned) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "eicFinishAddingEntries"));
+    }
+
+    optional<vector<uint8_t>> signature =
+            support::coseSignEcDsaWithSignature(signatureOfToBeSigned.value(),
+                                                encodedCbor,  // data
+                                                {});          // certificateChain
+    if (!signature) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED, "Error signing data"));
+    }
+
+    optional<vector<uint8_t>> encryptedCredentialKeys = hwProxy_->finishGetCredentialData(docType_);
+    if (!encryptedCredentialKeys) {
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificErrorWithMessage(
+                IIdentityCredentialStore::STATUS_FAILED,
+                "Error generating encrypted CredentialKeys"));
+    }
+    cppbor::Array array;
+    array.add(docType_);
+    array.add(testCredential_);
+    array.add(encryptedCredentialKeys.value());
+    vector<uint8_t> credentialData = array.encode();
+
+    *outCredentialData = credentialData;
+    *outProofOfProvisioningSignature = signature.value();
+    hwProxy_->shutdown();
+
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace aidl::android::hardware::identity
diff --git a/identity/aidl/default/common/WritableIdentityCredential.h b/identity/aidl/default/common/WritableIdentityCredential.h
new file mode 100644
index 0000000..36ad430
--- /dev/null
+++ b/identity/aidl/default/common/WritableIdentityCredential.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2019, 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_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+#define ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
+
+#include <aidl/android/hardware/identity/BnWritableIdentityCredential.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+
+#include <cppbor.h>
+#include <set>
+
+#include "IdentityCredentialStore.h"
+#include "SecureHardwareProxy.h"
+
+namespace aidl::android::hardware::identity {
+
+using ::android::sp;
+using ::android::hardware::identity::SecureHardwareProvisioningProxy;
+using ::std::set;
+using ::std::string;
+using ::std::vector;
+
+class WritableIdentityCredential : public BnWritableIdentityCredential {
+  public:
+    // For a new credential, call initialize() right after construction.
+    //
+    // For an updated credential, call initializeForUpdate() right after construction.
+    //
+    WritableIdentityCredential(sp<SecureHardwareProvisioningProxy> hwProxy, const string& docType,
+                               bool testCredential)
+        : hwProxy_(hwProxy), docType_(docType), testCredential_(testCredential) {}
+
+    ~WritableIdentityCredential();
+
+    // Creates the Credential Key. Returns false on failure.
+    bool initialize();
+
+    // Used when updating a credential. Returns false on failure.
+    bool initializeForUpdate(const vector<uint8_t>& encryptedCredentialKeys);
+
+    // Methods from IWritableIdentityCredential follow.
+    ndk::ScopedAStatus getAttestationCertificate(const vector<uint8_t>& attestationApplicationId,
+                                                 const vector<uint8_t>& attestationChallenge,
+                                                 vector<Certificate>* outCertificateChain) override;
+
+    ndk::ScopedAStatus setExpectedProofOfProvisioningSize(
+            int32_t expectedProofOfProvisioningSize) override;
+
+    ndk::ScopedAStatus startPersonalization(int32_t accessControlProfileCount,
+                                            const vector<int32_t>& entryCounts) override;
+
+    ndk::ScopedAStatus addAccessControlProfile(
+            int32_t id, const Certificate& readerCertificate, bool userAuthenticationRequired,
+            int64_t timeoutMillis, int64_t secureUserId,
+            SecureAccessControlProfile* outSecureAccessControlProfile) override;
+
+    ndk::ScopedAStatus beginAddEntry(const vector<int32_t>& accessControlProfileIds,
+                                     const string& nameSpace, const string& name,
+                                     int32_t entrySize) override;
+    ndk::ScopedAStatus addEntryValue(const vector<uint8_t>& content,
+                                     vector<uint8_t>* outEncryptedContent) override;
+
+    ndk::ScopedAStatus finishAddingEntries(
+            vector<uint8_t>* outCredentialData,
+            vector<uint8_t>* outProofOfProvisioningSignature) override;
+
+  private:
+    // Set by constructor.
+    sp<SecureHardwareProvisioningProxy> hwProxy_;
+    string docType_;
+    bool testCredential_;
+
+    // This is set in initialize().
+    bool startPersonalizationCalled_;
+    bool firstEntry_;
+
+    // This is set in getAttestationCertificate().
+    bool getAttestationCertificateAlreadyCalled_ = false;
+
+    // These fields are initialized during startPersonalization()
+    size_t numAccessControlProfileRemaining_;
+    vector<int32_t> remainingEntryCounts_;
+    cppbor::Array signedDataAccessControlProfiles_;
+    cppbor::Map signedDataNamespaces_;
+    cppbor::Array signedDataCurrentNamespace_;
+    size_t expectedProofOfProvisioningSize_;
+
+    // This field is initialized in addAccessControlProfile
+    set<int32_t> accessControlProfileIds_;
+
+    // These fields are initialized during beginAddEntry()
+    size_t entryRemainingBytes_;
+    string entryNameSpace_;
+    string entryName_;
+    vector<int32_t> entryAccessControlProfileIds_;
+    vector<uint8_t> entryBytes_;
+    set<string> allNameSpaces_;
+};
+
+}  // namespace aidl::android::hardware::identity
+
+#endif  // ANDROID_HARDWARE_IDENTITY_WRITABLEIDENTITYCREDENTIAL_H
diff --git a/identity/aidl/default/identity-default.xml b/identity/aidl/default/identity-default.xml
index a47d354..a074250 100644
--- a/identity/aidl/default/identity-default.xml
+++ b/identity/aidl/default/identity-default.xml
@@ -1,6 +1,7 @@
 <manifest version="1.0" type="device">
     <hal format="aidl">
         <name>android.hardware.identity</name>
+        <version>3</version>
         <interface>
             <name>IIdentityCredentialStore</name>
             <instance>default</instance>
diff --git a/identity/aidl/default/libeic/EicCbor.c b/identity/aidl/default/libeic/EicCbor.c
new file mode 100644
index 0000000..0e2684f
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright 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 "EicCbor.h"
+
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize) {
+    eicMemSet(cbor, '\0', sizeof(EicCbor));
+    cbor->size = 0;
+    cbor->bufferSize = bufferSize;
+    cbor->buffer = buffer;
+    cbor->digestType = EIC_CBOR_DIGEST_TYPE_SHA256;
+    eicOpsSha256Init(&cbor->digester.sha256);
+}
+
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+                           const uint8_t* hmacKey, size_t hmacKeySize) {
+    eicMemSet(cbor, '\0', sizeof(EicCbor));
+    cbor->size = 0;
+    cbor->bufferSize = bufferSize;
+    cbor->buffer = buffer;
+    cbor->digestType = EIC_CBOR_DIGEST_TYPE_HMAC_SHA256;
+    eicOpsHmacSha256Init(&cbor->digester.hmacSha256, hmacKey, hmacKeySize);
+}
+
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256) {
+    cbor->secondaryDigesterSha256 = sha256;
+}
+
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]) {
+    switch (cbor->digestType) {
+        case EIC_CBOR_DIGEST_TYPE_SHA256:
+            eicOpsSha256Final(&cbor->digester.sha256, digest);
+            break;
+        case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+            eicOpsHmacSha256Final(&cbor->digester.hmacSha256, digest);
+            break;
+    }
+}
+
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size) {
+    switch (cbor->digestType) {
+        case EIC_CBOR_DIGEST_TYPE_SHA256:
+            eicOpsSha256Update(&cbor->digester.sha256, data, size);
+            break;
+        case EIC_CBOR_DIGEST_TYPE_HMAC_SHA256:
+            eicOpsHmacSha256Update(&cbor->digester.hmacSha256, data, size);
+            break;
+    }
+    if (cbor->secondaryDigesterSha256 != NULL) {
+        eicOpsSha256Update(cbor->secondaryDigesterSha256, data, size);
+    }
+
+    if (cbor->size >= cbor->bufferSize) {
+        cbor->size += size;
+        return;
+    }
+
+    size_t numBytesLeft = cbor->bufferSize - cbor->size;
+    size_t numBytesToCopy = size;
+    if (numBytesToCopy > numBytesLeft) {
+        numBytesToCopy = numBytesLeft;
+    }
+    eicMemCpy(cbor->buffer + cbor->size, data, numBytesToCopy);
+
+    cbor->size += size;
+}
+
+size_t eicCborAdditionalLengthBytesFor(size_t size) {
+    if (size < 24) {
+        return 0;
+    } else if (size <= 0xff) {
+        return 1;
+    } else if (size <= 0xffff) {
+        return 2;
+    } else if (size <= 0xffffffff) {
+        return 4;
+    }
+    return 8;
+}
+
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size) {
+    uint8_t data[9];
+
+    if (size < 24) {
+        data[0] = (majorType << 5) | size;
+        eicCborAppend(cbor, data, 1);
+    } else if (size <= 0xff) {
+        data[0] = (majorType << 5) | 24;
+        data[1] = size;
+        eicCborAppend(cbor, data, 2);
+    } else if (size <= 0xffff) {
+        data[0] = (majorType << 5) | 25;
+        data[1] = size >> 8;
+        data[2] = size & 0xff;
+        eicCborAppend(cbor, data, 3);
+    } else if (size <= 0xffffffff) {
+        data[0] = (majorType << 5) | 26;
+        data[1] = (size >> 24) & 0xff;
+        data[2] = (size >> 16) & 0xff;
+        data[3] = (size >> 8) & 0xff;
+        data[4] = size & 0xff;
+        eicCborAppend(cbor, data, 5);
+    } else {
+        data[0] = (majorType << 5) | 27;
+        data[1] = (((uint64_t)size) >> 56) & 0xff;
+        data[2] = (((uint64_t)size) >> 48) & 0xff;
+        data[3] = (((uint64_t)size) >> 40) & 0xff;
+        data[4] = (((uint64_t)size) >> 32) & 0xff;
+        data[5] = (((uint64_t)size) >> 24) & 0xff;
+        data[6] = (((uint64_t)size) >> 16) & 0xff;
+        data[7] = (((uint64_t)size) >> 8) & 0xff;
+        data[8] = ((uint64_t)size) & 0xff;
+        eicCborAppend(cbor, data, 9);
+    }
+}
+
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dataSize);
+    eicCborAppend(cbor, data, dataSize);
+}
+
+void eicCborAppendString(EicCbor* cbor, const char* str) {
+    size_t length = eicStrLen(str);
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_STRING, length);
+    eicCborAppend(cbor, (const uint8_t*)str, length);
+}
+
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SIMPLE, simpleValue);
+}
+
+void eicCborAppendBool(EicCbor* cbor, bool value) {
+    uint8_t simpleValue = value ? EIC_CBOR_SIMPLE_VALUE_TRUE : EIC_CBOR_SIMPLE_VALUE_FALSE;
+    eicCborAppendSimple(cbor, simpleValue);
+}
+
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value) {
+    size_t encoded = value;
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_SEMANTIC, encoded);
+}
+
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value) {
+    size_t encoded = value;
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_UNSIGNED, encoded);
+}
+
+void eicCborAppendNumber(EicCbor* cbor, int64_t value) {
+    if (value < 0) {
+        size_t encoded = -1 - value;
+        eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_NEGATIVE, encoded);
+    } else {
+        eicCborAppendUnsigned(cbor, value);
+    }
+}
+
+void eicCborAppendArray(EicCbor* cbor, size_t numElements) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, numElements);
+}
+
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs) {
+    eicCborBegin(cbor, EIC_CBOR_MAJOR_TYPE_MAP, numPairs);
+}
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+                              size_t readerCertificateSize, bool userAuthenticationRequired,
+                              uint64_t timeoutMillis, uint64_t secureUserId) {
+    size_t numPairs = 1;
+    if (readerCertificateSize > 0) {
+        numPairs += 1;
+    }
+    if (userAuthenticationRequired) {
+        numPairs += 2;
+        if (secureUserId > 0) {
+            numPairs += 1;
+        }
+    }
+    eicCborAppendMap(cborBuilder, numPairs);
+    eicCborAppendString(cborBuilder, "id");
+    eicCborAppendUnsigned(cborBuilder, id);
+    if (readerCertificateSize > 0) {
+        eicCborAppendString(cborBuilder, "readerCertificate");
+        eicCborAppendByteString(cborBuilder, readerCertificate, readerCertificateSize);
+    }
+    if (userAuthenticationRequired) {
+        eicCborAppendString(cborBuilder, "userAuthenticationRequired");
+        eicCborAppendBool(cborBuilder, userAuthenticationRequired);
+        eicCborAppendString(cborBuilder, "timeoutMillis");
+        eicCborAppendUnsigned(cborBuilder, timeoutMillis);
+        if (secureUserId > 0) {
+            eicCborAppendString(cborBuilder, "secureUserId");
+            eicCborAppendUnsigned(cborBuilder, secureUserId);
+        }
+    }
+
+    if (cborBuilder->size > cborBuilder->bufferSize) {
+        eicDebug("Buffer for ACP CBOR is too small (%zd) - need %zd bytes", cborBuilder->bufferSize,
+                 cborBuilder->size);
+        return false;
+    }
+
+    return true;
+}
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+                                    size_t numAccessControlProfileIds, const char* nameSpace,
+                                    const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+                                    size_t* outAdditionalDataCborSize,
+                                    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]) {
+    EicCbor cborBuilder;
+
+    eicCborInit(&cborBuilder, cborBuffer, cborBufferSize);
+    eicCborAppendMap(&cborBuilder, 3);
+    eicCborAppendString(&cborBuilder, "Namespace");
+    eicCborAppendString(&cborBuilder, nameSpace);
+    eicCborAppendString(&cborBuilder, "Name");
+    eicCborAppendString(&cborBuilder, name);
+    eicCborAppendString(&cborBuilder, "AccessControlProfileIds");
+    eicCborAppendArray(&cborBuilder, numAccessControlProfileIds);
+    for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+        eicCborAppendNumber(&cborBuilder, accessControlProfileIds[n]);
+    }
+    if (cborBuilder.size > cborBufferSize) {
+        eicDebug("Not enough space for additionalData - buffer is only %zd bytes, content is %zd",
+                 cborBufferSize, cborBuilder.size);
+        return false;
+    }
+    if (outAdditionalDataCborSize != NULL) {
+        *outAdditionalDataCborSize = cborBuilder.size;
+    }
+    eicCborFinal(&cborBuilder, additionalDataSha256);
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicCbor.h b/identity/aidl/default/libeic/EicCbor.h
new file mode 100644
index 0000000..9c0f531
--- /dev/null
+++ b/identity/aidl/default/libeic/EicCbor.h
@@ -0,0 +1,167 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicOps.h"
+
+typedef enum {
+    EIC_CBOR_DIGEST_TYPE_SHA256,
+    EIC_CBOR_DIGEST_TYPE_HMAC_SHA256,
+} EicCborDigestType;
+
+/* EicCbor is a utility class to build CBOR data structures and calculate
+ * digests on the fly.
+ */
+typedef struct {
+    // Contains the size of the built CBOR, even if it exceeds bufferSize (will
+    // never write to buffer beyond bufferSize though)
+    size_t size;
+
+    // The size of the buffer. Is zero if no data is recorded in which case
+    // only digesting is performed.
+    size_t bufferSize;
+
+    // Whether we're producing a SHA-256 or HMAC-SHA256 digest.
+    EicCborDigestType digestType;
+
+    // The SHA-256 digester object.
+    union {
+        EicSha256Ctx sha256;
+        EicHmacSha256Ctx hmacSha256;
+    } digester;
+
+    // The secondary digester, may be unset.
+    EicSha256Ctx* secondaryDigesterSha256;
+
+    // The buffer used for building up CBOR or NULL if bufferSize is 0.
+    uint8_t* buffer;
+} EicCbor;
+
+/* Initializes an EicCbor.
+ *
+ * The given buffer will be used, up to bufferSize.
+ *
+ * If bufferSize is 0, buffer may be NULL.
+ */
+void eicCborInit(EicCbor* cbor, uint8_t* buffer, size_t bufferSize);
+
+/* Like eicCborInit() but uses HMAC-SHA256 instead of SHA-256.
+ */
+void eicCborInitHmacSha256(EicCbor* cbor, uint8_t* buffer, size_t bufferSize,
+                           const uint8_t* hmacKey, size_t hmacKeySize);
+
+/* Enables a secondary digester.
+ *
+ * May be enabled midway through processing, this can be used to e.g. calculate
+ * a digest of Sig_structure (for COSE_Sign1) and a separate digest of its
+ * payload.
+ */
+void eicCborEnableSecondaryDigesterSha256(EicCbor* cbor, EicSha256Ctx* sha256);
+
+/* Finishes building CBOR and returns the digest. */
+void eicCborFinal(EicCbor* cbor, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+/* Appends CBOR data to the EicCbor. */
+void eicCborAppend(EicCbor* cbor, const uint8_t* data, size_t size);
+
+#define EIC_CBOR_MAJOR_TYPE_UNSIGNED 0
+#define EIC_CBOR_MAJOR_TYPE_NEGATIVE 1
+#define EIC_CBOR_MAJOR_TYPE_BYTE_STRING 2
+#define EIC_CBOR_MAJOR_TYPE_STRING 3
+#define EIC_CBOR_MAJOR_TYPE_ARRAY 4
+#define EIC_CBOR_MAJOR_TYPE_MAP 5
+#define EIC_CBOR_MAJOR_TYPE_SEMANTIC 6
+#define EIC_CBOR_MAJOR_TYPE_SIMPLE 7
+
+#define EIC_CBOR_SIMPLE_VALUE_FALSE 20
+#define EIC_CBOR_SIMPLE_VALUE_TRUE 21
+
+#define EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR 24
+
+/* Begins a new CBOR value. */
+void eicCborBegin(EicCbor* cbor, int majorType, size_t size);
+
+/* Appends a bytestring. */
+void eicCborAppendByteString(EicCbor* cbor, const uint8_t* data, size_t dataSize);
+
+/* Appends a NUL-terminated UTF-8 string. */
+void eicCborAppendString(EicCbor* cbor, const char* str);
+
+/* Appends a simple value. */
+void eicCborAppendSimple(EicCbor* cbor, uint8_t simpleValue);
+
+/* Appends a boolean. */
+void eicCborAppendBool(EicCbor* cbor, bool value);
+
+/* Appends a semantic */
+void eicCborAppendSemantic(EicCbor* cbor, uint64_t value);
+
+/* Appends an unsigned number. */
+void eicCborAppendUnsigned(EicCbor* cbor, uint64_t value);
+
+/* Appends a number. */
+void eicCborAppendNumber(EicCbor* cbor, int64_t value);
+
+/* Starts appending an array.
+ *
+ * After this numElements CBOR elements must follow.
+ */
+void eicCborAppendArray(EicCbor* cbor, size_t numElements);
+
+/* Starts appending a map.
+ *
+ * After this numPairs pairs of CBOR elements must follow.
+ */
+void eicCborAppendMap(EicCbor* cbor, size_t numPairs);
+
+/* Calculates how many bytes are needed to store a size. */
+size_t eicCborAdditionalLengthBytesFor(size_t size);
+
+bool eicCborCalcAccessControl(EicCbor* cborBuilder, int id, const uint8_t* readerCertificate,
+                              size_t readerCertificateSize, bool userAuthenticationRequired,
+                              uint64_t timeoutMillis, uint64_t secureUserId);
+
+bool eicCborCalcEntryAdditionalData(const int* accessControlProfileIds,
+                                    size_t numAccessControlProfileIds, const char* nameSpace,
+                                    const char* name, uint8_t* cborBuffer, size_t cborBufferSize,
+                                    size_t* outAdditionalDataCborSize,
+                                    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE]);
+
+// The maximum size of an encoded Secure Access Control Profile that we
+// support. Since the SACP may contain a reader certificate chain these can get
+// pretty big.
+//
+// Currently we allocate space on the stack for this structure which is why we
+// have a maximum size. We can get rid of the maximum size by incrementally
+// building/verifying the SACP. TODO: actually do this.
+//
+#define EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE 512
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_CBOR_H
diff --git a/identity/aidl/default/libeic/EicOps.h b/identity/aidl/default/libeic/EicOps.h
new file mode 100644
index 0000000..d4fcf0e
--- /dev/null
+++ b/identity/aidl/default/libeic/EicOps.h
@@ -0,0 +1,302 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
+
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+// Uncomment or define if debug messages are needed.
+//
+//#define EIC_DEBUG
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// The following defines must be set to something appropriate
+//
+//   EIC_SHA256_CONTEXT_SIZE - the size of EicSha256Ctx
+//   EIC_HMAC_SHA256_CONTEXT_SIZE - the size of EicHmacSha256Ctx
+//
+// For example, if EicSha256Ctx is implemented using BoringSSL this would be defined
+// as sizeof(SHA256_CTX).
+//
+// We expect the implementation to provide a header file with the name
+// EicOpsImpl.h to do all this.
+//
+#include "EicOpsImpl.h"
+
+#define EIC_SHA256_DIGEST_SIZE 32
+
+// The size of a P-256 private key.
+//
+#define EIC_P256_PRIV_KEY_SIZE 32
+
+// The size of a P-256 public key in uncompressed form.
+//
+// The public key is stored in uncompressed form, first the X coordinate, then
+// the Y coordinate.
+//
+#define EIC_P256_PUB_KEY_SIZE 64
+
+// Size of one of the coordinates in a curve-point.
+//
+#define EIC_P256_COORDINATE_SIZE 32
+
+// The size of an ECSDA signature using P-256.
+//
+// The R and S values are stored here, first R then S.
+//
+#define EIC_ECDSA_P256_SIGNATURE_SIZE 64
+
+#define EIC_AES_128_KEY_SIZE 16
+
+// The following are definitions of implementation functions the
+// underlying platform must provide.
+//
+
+struct EicSha256Ctx {
+    uint8_t reserved[EIC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicSha256Ctx EicSha256Ctx;
+
+struct EicHmacSha256Ctx {
+    uint8_t reserved[EIC_HMAC_SHA256_CONTEXT_SIZE];
+};
+typedef struct EicHmacSha256Ctx EicHmacSha256Ctx;
+
+#ifdef EIC_DEBUG
+// Debug macro. Don't include a new-line in message.
+//
+#define eicDebug(...)                            \
+    do {                                         \
+        eicPrint("%s:%d: ", __FILE__, __LINE__); \
+        eicPrint(__VA_ARGS__);                   \
+        eicPrint("\n");                          \
+    } while (0)
+#else
+#define eicDebug(...) \
+    do {              \
+    } while (0)
+#endif
+
+// Prints message which should include new-line character. Can be no-op.
+//
+// Don't use this from code, use eicDebug() instead.
+//
+#ifdef EIC_DEBUG
+void eicPrint(const char* format, ...);
+#else
+inline void eicPrint(const char*, ...) {}
+#endif
+
+// Dumps data as pretty-printed hex. Can be no-op.
+//
+#ifdef EIC_DEBUG
+void eicHexdump(const char* message, const uint8_t* data, size_t dataSize);
+#else
+inline void eicHexdump(const char*, const uint8_t*, size_t) {}
+#endif
+
+// Pretty-prints encoded CBOR. Can be no-op.
+//
+// If a byte-string is larger than |maxBStrSize| its contents will not be
+// printed, instead the value of the form "<bstr size=1099016
+// sha1=ef549cca331f73dfae2090e6a37c04c23f84b07b>" will be printed. Pass zero
+// for |maxBStrSize| to disable this.
+//
+#ifdef EIC_DEBUG
+void eicCborPrettyPrint(const uint8_t* cborData, size_t cborDataSize, size_t maxBStrSize);
+#else
+inline void eicCborPrettyPrint(const uint8_t*, size_t, size_t) {}
+#endif
+
+// Memory setting, see memset(3).
+void* eicMemSet(void* s, int c, size_t n);
+
+// Memory copying, see memcpy(3).
+void* eicMemCpy(void* dest, const void* src, size_t n);
+
+// String length, see strlen(3).
+size_t eicStrLen(const char* s);
+
+// Memory compare, see CRYPTO_memcmp(3SSL)
+//
+// It takes an amount of time dependent on len, but independent of the contents of the
+// memory regions pointed to by s1 and s2.
+//
+int eicCryptoMemCmp(const void* s1, const void* s2, size_t n);
+
+// Random number generation.
+bool eicOpsRandom(uint8_t* buf, size_t numBytes);
+
+// If |testCredential| is true, returns the 128-bit AES Hardware-Bound Key (16 bytes).
+//
+// Otherwise returns all zeroes (16 bytes).
+//
+const uint8_t* eicOpsGetHardwareBoundKey(bool testCredential);
+
+// Encrypts |data| with |key| and |additionalAuthenticatedData| using |nonce|,
+// returns the resulting (nonce || ciphertext || tag) in |encryptedData| which
+// must be of size |dataSize| + 28.
+bool eicOpsEncryptAes128Gcm(
+        const uint8_t* key,    // Must be 16 bytes
+        const uint8_t* nonce,  // Must be 12 bytes
+        const uint8_t* data,   // May be NULL if size is 0
+        size_t dataSize,
+        const uint8_t* additionalAuthenticationData,  // May be NULL if size is 0
+        size_t additionalAuthenticationDataSize, uint8_t* encryptedData);
+
+// Decrypts |encryptedData| using |key| and |additionalAuthenticatedData|,
+// returns resulting plaintext in |data| must be of size |encryptedDataSize| - 28.
+//
+// The format of |encryptedData| must be as specified in the
+// encryptAes128Gcm() function.
+bool eicOpsDecryptAes128Gcm(const uint8_t* key,  // Must be 16 bytes
+                            const uint8_t* encryptedData, size_t encryptedDataSize,
+                            const uint8_t* additionalAuthenticationData,
+                            size_t additionalAuthenticationDataSize, uint8_t* data);
+
+// Creates an EC key using the P-256 curve. The private key is written to
+// |privateKey|. The public key is written to |publicKey|.
+//
+bool eicOpsCreateEcKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                       uint8_t publicKey[EIC_P256_PUB_KEY_SIZE]);
+
+// Generates CredentialKey plus an attestation certificate.
+//
+// The attestation certificate will be signed by the attestation keys the secure
+// area has been provisioned with. The given |challenge| and |applicationId|
+// will be used as will |testCredential|.
+//
+// The generated certificate will be in X.509 format and returned in |cert|
+// and |certSize| must be set to the size of this array and this function will
+// set it to the size of the certification chain on successfully return.
+//
+// This may return either a single certificate or an entire certificate
+// chain. If it returns only a single certificate, the implementation of
+// SecureHardwareProvisioningProxy::createCredentialKey() should amend the
+// remainder of the certificate chain on the HAL side.
+//
+bool eicOpsCreateCredentialKey(uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE], const uint8_t* challenge,
+                               size_t challengeSize, const uint8_t* applicationId,
+                               size_t applicationIdSize, bool testCredential, uint8_t* cert,
+                               size_t* certSize);  // inout
+
+// Generate an X.509 certificate for the key identified by |publicKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+// If proofOfBinding is not NULL, it will be included as an OCTET_STRING
+// X.509 extension at OID 1.3.6.1.4.1.11129.2.1.26.
+//
+// The certificate will be signed by the key identified by |signingKey| which
+// must be of the form returned by eicOpsCreateEcKey().
+//
+bool eicOpsSignEcKey(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                     const uint8_t signingKey[EIC_P256_PRIV_KEY_SIZE], unsigned int serial,
+                     const char* issuerName, const char* subjectName, time_t validityNotBefore,
+                     time_t validityNotAfter, const uint8_t* proofOfBinding,
+                     size_t proofOfBindingSize, uint8_t* cert, size_t* certSize);  // inout
+
+// Uses |privateKey| to create an ECDSA signature of some data (the SHA-256 must
+// be given by |digestOfData|). Returns the signature in |signature|.
+//
+bool eicOpsEcDsa(const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                 const uint8_t digestOfData[EIC_SHA256_DIGEST_SIZE],
+                 uint8_t signature[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// Performs Elliptic Curve Diffie-Helman.
+//
+bool eicOpsEcdh(const uint8_t publicKey[EIC_P256_PUB_KEY_SIZE],
+                const uint8_t privateKey[EIC_P256_PRIV_KEY_SIZE],
+                uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE]);
+
+// Performs HKDF.
+//
+bool eicOpsHkdf(const uint8_t* sharedSecret, size_t sharedSecretSize, const uint8_t* salt,
+                size_t saltSize, const uint8_t* info, size_t infoSize, uint8_t* output,
+                size_t outputSize);
+
+// SHA-256 functions.
+void eicOpsSha256Init(EicSha256Ctx* ctx);
+void eicOpsSha256Update(EicSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsSha256Final(EicSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// HMAC SHA-256 functions.
+void eicOpsHmacSha256Init(EicHmacSha256Ctx* ctx, const uint8_t* key, size_t keySize);
+void eicOpsHmacSha256Update(EicHmacSha256Ctx* ctx, const uint8_t* data, size_t len);
+void eicOpsHmacSha256Final(EicHmacSha256Ctx* ctx, uint8_t digest[EIC_SHA256_DIGEST_SIZE]);
+
+// Extracts the public key in the given X.509 certificate.
+//
+// If the key is not an EC key, this function fails.
+//
+// Otherwise the public key is stored in uncompressed form in |publicKey| which
+// size should be set in |publicKeySize|. On successful return |publicKeySize|
+// is set to the length of the key. If there is not enough space, the function
+// fails.
+//
+// (The public key returned is not necessarily a P-256 key, even if it is note
+// that its size is not EIC_P256_PUBLIC_KEY_SIZE because of the leading 0x04.)
+//
+bool eicOpsX509GetPublicKey(const uint8_t* x509Cert, size_t x509CertSize, uint8_t* publicKey,
+                            size_t* publicKeySize);
+
+// Checks that the X.509 certificate given by |x509Cert| is signed by the public
+// key given by |publicKey| which must be an EC key in uncompressed form (e.g.
+// same formatt as returned by eicOpsX509GetPublicKey()).
+//
+bool eicOpsX509CertSignedByPublicKey(const uint8_t* x509Cert, size_t x509CertSize,
+                                     const uint8_t* publicKey, size_t publicKeySize);
+
+// Checks that |signature| is a signature of some data (given by |digest|),
+// signed by the public key given by |publicKey|.
+//
+// The key must be an EC key in uncompressed form (e.g.  same format as returned
+// by eicOpsX509GetPublicKey()).
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// The size of digest must match the size of the key.
+//
+bool eicOpsEcDsaVerifyWithPublicKey(const uint8_t* digest, size_t digestSize,
+                                    const uint8_t* signature, size_t signatureSize,
+                                    const uint8_t* publicKey, size_t publicKeySize);
+
+// Validates that the passed in data constitutes a valid auth- and verification tokens.
+//
+bool eicOpsValidateAuthToken(uint64_t challenge, uint64_t secureUserId, uint64_t authenticatorId,
+                             int hardwareAuthenticatorType, uint64_t timeStamp, const uint8_t* mac,
+                             size_t macSize, uint64_t verificationTokenChallenge,
+                             uint64_t verificationTokenTimeStamp,
+                             int verificationTokenSecurityLevel,
+                             const uint8_t* verificationTokenMac, size_t verificationTokenMacSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_OPS_H
diff --git a/identity/aidl/default/libeic/EicPresentation.c b/identity/aidl/default/libeic/EicPresentation.c
new file mode 100644
index 0000000..3d13766
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright 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 "EicPresentation.h"
+
+#include <inttypes.h>
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+                         const uint8_t* encryptedCredentialKeys,
+                         size_t encryptedCredentialKeysSize) {
+    uint8_t credentialKeys[86];
+    bool expectPopSha256 = false;
+
+    // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+    // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+    // to support loading all feature versions.
+    //
+    if (encryptedCredentialKeysSize == 52 + 28) {
+        /* do nothing */
+    } else if (encryptedCredentialKeysSize == 86 + 28) {
+        expectPopSha256 = true;
+    } else {
+        eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+        return false;
+    }
+
+    eicMemSet(ctx, '\0', sizeof(EicPresentation));
+
+    if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+                                encryptedCredentialKeysSize,
+                                // DocType is the additionalAuthenticatedData
+                                (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+        eicDebug("Error decrypting CredentialKeys");
+        return false;
+    }
+
+    // It's supposed to look like this;
+    //
+    // Feature version 202009:
+    //
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
+    //         ]
+    //
+    // Feature version 202101:
+    //
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
+    //              bstr    ; proofOfProvisioning SHA-256
+    //         ]
+    //
+    // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+    // SHA-256 is 32 bytes.
+    //
+    if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) ||  // array of two or three elements
+        credentialKeys[1] != 0x50 ||                             // 16-byte bstr
+        credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) {  // 32-byte bstr
+        eicDebug("Invalid CBOR for CredentialKeys");
+        return false;
+    }
+    if (expectPopSha256) {
+        if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) {  // 32-byte bstr
+            eicDebug("Invalid CBOR for CredentialKeys");
+            return false;
+        }
+    }
+    eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+    eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+    ctx->testCredential = testCredential;
+    if (expectPopSha256) {
+        eicMemCpy(ctx->proofOfProvisioningSha256, credentialKeys + 54, EIC_SHA256_DIGEST_SIZE);
+    }
+    return true;
+}
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+                                           uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+                                           uint8_t signingKeyBlob[60]) {
+    uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+    uint8_t signingKeyPub[EIC_P256_PUB_KEY_SIZE];
+    uint8_t cborBuf[64];
+
+    // Generate the ProofOfBinding CBOR to include in the X.509 certificate in
+    // IdentityCredentialAuthenticationKeyExtension CBOR. This CBOR is defined
+    // by the following CDDL
+    //
+    //   ProofOfBinding = [
+    //     "ProofOfBinding",
+    //     bstr,                  // Contains the SHA-256 of ProofOfProvisioning
+    //   ]
+    //
+    // This array may grow in the future if other information needs to be
+    // conveyed.
+    //
+    // The bytes of ProofOfBinding is is represented as an OCTET_STRING
+    // and stored at OID 1.3.6.1.4.1.11129.2.1.26.
+    //
+
+    EicCbor cbor;
+    eicCborInit(&cbor, cborBuf, sizeof cborBuf);
+    eicCborAppendArray(&cbor, 2);
+    eicCborAppendString(&cbor, "ProofOfBinding");
+    eicCborAppendByteString(&cbor, ctx->proofOfProvisioningSha256, EIC_SHA256_DIGEST_SIZE);
+    if (cbor.size > sizeof(cborBuf)) {
+        eicDebug("Exceeded buffer size");
+        return false;
+    }
+    const uint8_t* proofOfBinding = cborBuf;
+    size_t proofOfBindingSize = cbor.size;
+
+    if (!eicOpsCreateEcKey(signingKeyPriv, signingKeyPub)) {
+        eicDebug("Error creating signing key");
+        return false;
+    }
+
+    const int secondsInOneYear = 365 * 24 * 60 * 60;
+    time_t validityNotBefore = now;
+    time_t validityNotAfter = now + secondsInOneYear;  // One year from now.
+    if (!eicOpsSignEcKey(signingKeyPub, ctx->credentialPrivateKey, 1,
+                         "Android Identity Credential Key",                 // issuer CN
+                         "Android Identity Credential Authentication Key",  // subject CN
+                         validityNotBefore, validityNotAfter, proofOfBinding, proofOfBindingSize,
+                         publicKeyCert, publicKeyCertSize)) {
+        eicDebug("Error creating certificate for signing key");
+        return false;
+    }
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        eicDebug("Error getting random");
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, signingKeyPriv, sizeof(signingKeyPriv),
+                                // DocType is the additionalAuthenticatedData
+                                (const uint8_t*)docType, eicStrLen(docType), signingKeyBlob)) {
+        eicDebug("Error encrypting signing key");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+                                           uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]) {
+    uint8_t ephemeralPublicKey[EIC_P256_PUB_KEY_SIZE];
+    if (!eicOpsCreateEcKey(ctx->ephemeralPrivateKey, ephemeralPublicKey)) {
+        eicDebug("Error creating ephemeral key");
+        return false;
+    }
+    eicMemCpy(ephemeralPrivateKey, ctx->ephemeralPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+    return true;
+}
+
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge) {
+    do {
+        if (!eicOpsRandom((uint8_t*)&(ctx->authChallenge), sizeof(uint64_t))) {
+            eicDebug("Failed generating random challenge");
+            return false;
+        }
+    } while (ctx->authChallenge == 0);
+    eicDebug("Created auth challenge %" PRIu64, ctx->authChallenge);
+    *authChallenge = ctx->authChallenge;
+    return true;
+}
+
+// From "COSE Algorithms" registry
+//
+#define COSE_ALG_ECDSA_256 -7
+
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                                           size_t sessionTranscriptSize,
+                                           const uint8_t* requestMessage, size_t requestMessageSize,
+                                           int coseSignAlg,
+                                           const uint8_t* readerSignatureOfToBeSigned,
+                                           size_t readerSignatureOfToBeSignedSize) {
+    if (ctx->readerPublicKeySize == 0) {
+        eicDebug("No public key for reader");
+        return false;
+    }
+
+    // Right now we only support ECDSA with SHA-256 (e.g. ES256).
+    //
+    if (coseSignAlg != COSE_ALG_ECDSA_256) {
+        eicDebug(
+                "COSE Signature algorithm for reader signature is %d, "
+                "only ECDSA with SHA-256 is supported right now",
+                coseSignAlg);
+        return false;
+    }
+
+    // What we're going to verify is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    //   Sig_structure = [
+    //     context : "Signature" / "Signature1" / "CounterSignature",
+    //     body_protected : empty_or_serialized_map,
+    //     ? sign_protected : empty_or_serialized_map,
+    //     external_aad : bstr,
+    //     payload : bstr
+    //   ]
+    //
+    // So we're going to build that CBOR...
+    //
+    EicCbor cbor;
+    eicCborInit(&cbor, NULL, 0);
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> coseSignAlg (e.g. -7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // External_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time... the CBOR to be written is
+    //
+    //   ReaderAuthentication = [
+    //      "ReaderAuthentication",
+    //      SessionTranscript,
+    //      ItemsRequestBytes
+    //   ]
+    //
+    //   ItemsRequestBytes = #6.24(bstr .cbor ItemsRequest)
+    //
+    //   ReaderAuthenticationBytes = #6.24(bstr .cbor ReaderAuthentication)
+    //
+    // which is easily calculated below
+    //
+    size_t calculatedSize = 0;
+    calculatedSize += 1;  // Array of size 3
+    calculatedSize += 1;  // "ReaderAuthentication" less than 24 bytes
+    calculatedSize += sizeof("ReaderAuthentication") - 1;  // Don't include trailing NUL
+    calculatedSize += sessionTranscriptSize;               // Already CBOR encoded
+    calculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(requestMessageSize);
+    calculatedSize += requestMessageSize;
+
+    // However note that we're authenticating ReaderAuthenticationBytes which
+    // is a tagged bstr of the bytes of ReaderAuthentication. So need to get
+    // that in front.
+    size_t rabCalculatedSize = 0;
+    rabCalculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    rabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+    rabCalculatedSize += calculatedSize;
+
+    // Begin the bytestring for ReaderAuthenticationBytes;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, rabCalculatedSize);
+
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+    // Begins the bytestring for ReaderAuthentication;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+    // And now that we know the size, let's fill it in...
+    //
+    size_t payloadOffset = cbor.size;
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_ARRAY, 3);
+    eicCborAppendString(&cbor, "ReaderAuthentication");
+    eicCborAppend(&cbor, sessionTranscript, sessionTranscriptSize);
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, requestMessageSize);
+    eicCborAppend(&cbor, requestMessage, requestMessageSize);
+
+    if (cbor.size != payloadOffset + calculatedSize) {
+        eicDebug("CBOR size is %zd but we expected %zd", cbor.size, payloadOffset + calculatedSize);
+        return false;
+    }
+    uint8_t toBeSignedDigest[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, toBeSignedDigest);
+
+    if (!eicOpsEcDsaVerifyWithPublicKey(
+                toBeSignedDigest, EIC_SHA256_DIGEST_SIZE, readerSignatureOfToBeSigned,
+                readerSignatureOfToBeSignedSize, ctx->readerPublicKey, ctx->readerPublicKeySize)) {
+        eicDebug("Request message is not signed by public key");
+        return false;
+    }
+    ctx->requestMessageValidated = true;
+    return true;
+}
+
+// Validates the next certificate in the reader certificate chain.
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+                                   size_t certX509Size) {
+    // If we had a previous certificate, use its public key to validate this certificate.
+    if (ctx->readerPublicKeySize > 0) {
+        if (!eicOpsX509CertSignedByPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+                                             ctx->readerPublicKeySize)) {
+            eicDebug("Certificate is not signed by public key in the previous certificate");
+            return false;
+        }
+    }
+
+    // Store the key of this certificate, this is used to validate the next certificate
+    // and also ACPs with certificates that use the same public key...
+    ctx->readerPublicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+    if (!eicOpsX509GetPublicKey(certX509, certX509Size, ctx->readerPublicKey,
+                                &ctx->readerPublicKeySize)) {
+        eicDebug("Error extracting public key from certificate");
+        return false;
+    }
+    if (ctx->readerPublicKeySize == 0) {
+        eicDebug("Zero-length public key in certificate");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+                                 uint64_t authenticatorId, int hardwareAuthenticatorType,
+                                 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+                                 uint64_t verificationTokenChallenge,
+                                 uint64_t verificationTokenTimestamp,
+                                 int verificationTokenSecurityLevel,
+                                 const uint8_t* verificationTokenMac,
+                                 size_t verificationTokenMacSize) {
+    // It doesn't make sense to accept any tokens if eicPresentationCreateAuthChallenge()
+    // was never called.
+    if (ctx->authChallenge == 0) {
+        eicDebug("Trying validate tokens when no auth-challenge was previously generated");
+        return false;
+    }
+    // At least the verification-token must have the same challenge as what was generated.
+    if (verificationTokenChallenge != ctx->authChallenge) {
+        eicDebug("Challenge in verification token does not match the challenge "
+                 "previously generated");
+        return false;
+    }
+    if (!eicOpsValidateAuthToken(
+                challenge, secureUserId, authenticatorId, hardwareAuthenticatorType, timeStamp, mac,
+                macSize, verificationTokenChallenge, verificationTokenTimestamp,
+                verificationTokenSecurityLevel, verificationTokenMac, verificationTokenMacSize)) {
+        return false;
+    }
+    ctx->authTokenChallenge = challenge;
+    ctx->authTokenSecureUserId = secureUserId;
+    ctx->authTokenTimestamp = timeStamp;
+    ctx->verificationTokenTimestamp = verificationTokenTimestamp;
+    return true;
+}
+
+static bool checkUserAuth(EicPresentation* ctx, bool userAuthenticationRequired, int timeoutMillis,
+                          uint64_t secureUserId) {
+    if (!userAuthenticationRequired) {
+        return true;
+    }
+
+    if (secureUserId != ctx->authTokenSecureUserId) {
+        eicDebug("secureUserId in profile differs from userId in authToken");
+        return false;
+    }
+
+    // Only ACP with auth-on-every-presentation - those with timeout == 0 - need the
+    // challenge to match...
+    if (timeoutMillis == 0) {
+        if (ctx->authTokenChallenge != ctx->authChallenge) {
+            eicDebug("Challenge in authToken (%" PRIu64
+                     ") doesn't match the challenge "
+                     "that was created (%" PRIu64 ") for this session",
+                     ctx->authTokenChallenge, ctx->authChallenge);
+            return false;
+        }
+    }
+
+    uint64_t now = ctx->verificationTokenTimestamp;
+    if (ctx->authTokenTimestamp > now) {
+        eicDebug("Timestamp in authToken is in the future");
+        return false;
+    }
+
+    if (timeoutMillis > 0) {
+        if (now > ctx->authTokenTimestamp + timeoutMillis) {
+            eicDebug("Deadline for authToken is in the past");
+            return false;
+        }
+    }
+
+    return true;
+}
+
+static bool checkReaderAuth(EicPresentation* ctx, const uint8_t* readerCertificate,
+                            size_t readerCertificateSize) {
+    uint8_t publicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+    size_t publicKeySize;
+
+    if (readerCertificateSize == 0) {
+        return true;
+    }
+
+    // Remember in this case certificate equality is done by comparing public
+    // keys, not bitwise comparison of the certificates.
+    //
+    publicKeySize = EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE;
+    if (!eicOpsX509GetPublicKey(readerCertificate, readerCertificateSize, publicKey,
+                                &publicKeySize)) {
+        eicDebug("Error extracting public key from certificate");
+        return false;
+    }
+    if (publicKeySize == 0) {
+        eicDebug("Zero-length public key in certificate");
+        return false;
+    }
+
+    if ((ctx->readerPublicKeySize != publicKeySize) ||
+        (eicCryptoMemCmp(ctx->readerPublicKey, publicKey, ctx->readerPublicKeySize) != 0)) {
+        return false;
+    }
+    return true;
+}
+
+// Note: This function returns false _only_ if an error occurred check for access, _not_
+// whether access is granted. Whether access is granted is returned in |accessGranted|.
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+                                                 const uint8_t* readerCertificate,
+                                                 size_t readerCertificateSize,
+                                                 bool userAuthenticationRequired, int timeoutMillis,
+                                                 uint64_t secureUserId, const uint8_t mac[28],
+                                                 bool* accessGranted) {
+    *accessGranted = false;
+
+    if (id < 0 || id >= 32) {
+        eicDebug("id value of %d is out of allowed range [0, 32[", id);
+        return false;
+    }
+
+    // Validate the MAC
+    uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+    EicCbor cborBuilder;
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis, secureUserId)) {
+        return false;
+    }
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, mac, 28, cborBuilder.buffer, cborBuilder.size,
+                                NULL)) {
+        eicDebug("MAC for AccessControlProfile doesn't match");
+        return false;
+    }
+
+    bool passedUserAuth =
+            checkUserAuth(ctx, userAuthenticationRequired, timeoutMillis, secureUserId);
+    bool passedReaderAuth = checkReaderAuth(ctx, readerCertificate, readerCertificateSize);
+
+    ctx->accessControlProfileMaskValidated |= (1 << id);
+    if (readerCertificateSize > 0) {
+        ctx->accessControlProfileMaskUsesReaderAuth |= (1 << id);
+    }
+    if (!passedReaderAuth) {
+        ctx->accessControlProfileMaskFailedReaderAuth |= (1 << id);
+    }
+    if (!passedUserAuth) {
+        ctx->accessControlProfileMaskFailedUserAuth |= (1 << id);
+    }
+
+    if (passedUserAuth && passedReaderAuth) {
+        *accessGranted = true;
+        eicDebug("Access granted for id %d", id);
+    }
+    return true;
+}
+
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                               size_t sessionTranscriptSize,
+                               const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+                               const uint8_t signingKeyBlob[60], const char* docType,
+                               unsigned int numNamespacesWithValues,
+                               size_t expectedDeviceNamespacesSize) {
+    uint8_t signingKeyPriv[EIC_P256_PRIV_KEY_SIZE];
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, signingKeyBlob, 60, (const uint8_t*)docType,
+                                eicStrLen(docType), signingKeyPriv)) {
+        eicDebug("Error decrypting signingKeyBlob");
+        return false;
+    }
+
+    uint8_t sharedSecret[EIC_P256_COORDINATE_SIZE];
+    if (!eicOpsEcdh(readerEphemeralPublicKey, signingKeyPriv, sharedSecret)) {
+        eicDebug("ECDH failed");
+        return false;
+    }
+
+    EicCbor cbor;
+    eicCborInit(&cbor, NULL, 0);
+    eicCborAppendSemantic(&cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborAppendByteString(&cbor, sessionTranscript, sessionTranscriptSize);
+    uint8_t salt[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, salt);
+
+    const uint8_t info[7] = {'E', 'M', 'a', 'c', 'K', 'e', 'y'};
+    uint8_t derivedKey[32];
+    if (!eicOpsHkdf(sharedSecret, EIC_P256_COORDINATE_SIZE, salt, sizeof(salt), info, sizeof(info),
+                    derivedKey, sizeof(derivedKey))) {
+        eicDebug("HKDF failed");
+        return false;
+    }
+
+    eicCborInitHmacSha256(&ctx->cbor, NULL, 0, derivedKey, sizeof(derivedKey));
+    ctx->buildCbor = true;
+
+    // What we're going to calculate the HMAC-SHA256 is the COSE ToBeMaced
+    // structure which looks like the following:
+    //
+    // MAC_structure = [
+    //   context : "MAC" / "MAC0",
+    //   protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    // ]
+    //
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "MAC0");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_HMAC_256_256 (5). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x05};
+    eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time... the CBOR to be written is
+    //
+    //   DeviceAuthentication = [
+    //      "DeviceAuthentication",
+    //      SessionTranscript,
+    //      DocType,                ; DocType as used in Documents structure in OfflineResponse
+    //      DeviceNameSpacesBytes
+    //   ]
+    //
+    //   DeviceNameSpacesBytes = #6.24(bstr .cbor DeviceNameSpaces)
+    //
+    //   DeviceAuthenticationBytes = #6.24(bstr .cbor DeviceAuthentication)
+    //
+    // which is easily calculated below
+    //
+    size_t calculatedSize = 0;
+    calculatedSize += 1;  // Array of size 4
+    calculatedSize += 1;  // "DeviceAuthentication" less than 24 bytes
+    calculatedSize += sizeof("DeviceAuthentication") - 1;  // Don't include trailing NUL
+    calculatedSize += sessionTranscriptSize;               // Already CBOR encoded
+    size_t docTypeLen = eicStrLen(docType);
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(docTypeLen) + docTypeLen;
+    calculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    calculatedSize += 1 + eicCborAdditionalLengthBytesFor(expectedDeviceNamespacesSize);
+    calculatedSize += expectedDeviceNamespacesSize;
+
+    // However note that we're authenticating DeviceAuthenticationBytes which
+    // is a tagged bstr of the bytes of DeviceAuthentication. So need to get
+    // that in front.
+    size_t dabCalculatedSize = 0;
+    dabCalculatedSize += 2;  // Semantic tag EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR (24)
+    dabCalculatedSize += 1 + eicCborAdditionalLengthBytesFor(calculatedSize);
+    dabCalculatedSize += calculatedSize;
+
+    // Begin the bytestring for DeviceAuthenticationBytes;
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, dabCalculatedSize);
+
+    eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+
+    // Begins the bytestring for DeviceAuthentication;
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, calculatedSize);
+
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "DeviceAuthentication");
+    eicCborAppend(&ctx->cbor, sessionTranscript, sessionTranscriptSize);
+    eicCborAppendString(&ctx->cbor, docType);
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborAppendSemantic(&ctx->cbor, EIC_CBOR_SEMANTIC_TAG_ENCODED_CBOR);
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedDeviceNamespacesSize);
+    ctx->expectedCborSizeAtEnd = expectedDeviceNamespacesSize + ctx->cbor.size;
+
+    eicCborAppendMap(&ctx->cbor, numNamespacesWithValues);
+    return true;
+}
+
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx) {
+    // HAL may use this object multiple times to retrieve data so need to reset various
+    // state objects here.
+    ctx->requestMessageValidated = false;
+    ctx->buildCbor = false;
+    ctx->accessControlProfileMaskValidated = 0;
+    ctx->accessControlProfileMaskUsesReaderAuth = 0;
+    ctx->accessControlProfileMaskFailedReaderAuth = 0;
+    ctx->accessControlProfileMaskFailedUserAuth = 0;
+    ctx->readerPublicKeySize = 0;
+    return true;
+}
+
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+        EicPresentation* ctx, const char* nameSpace, const char* name,
+        unsigned int newNamespaceNumEntries, int32_t /* entrySize */,
+        const int* accessControlProfileIds, size_t numAccessControlProfileIds,
+        uint8_t* scratchSpace, size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    if (newNamespaceNumEntries > 0) {
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        eicCborAppendMap(&ctx->cbor, newNamespaceNumEntries);
+    }
+
+    // We'll need to calc and store a digest of additionalData to check that it's the same
+    // additionalData being passed in for every eicPresentationRetrieveEntryValue() call...
+    //
+    ctx->accessCheckOk = false;
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        ctx->additionalDataSha256)) {
+        return EIC_ACCESS_CHECK_RESULT_FAILED;
+    }
+
+    if (numAccessControlProfileIds == 0) {
+        return EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES;
+    }
+
+    // Access is granted if at least one of the profiles grants access.
+    //
+    // If an item is configured without any profiles, access is denied.
+    //
+    EicAccessCheckResult result = EIC_ACCESS_CHECK_RESULT_FAILED;
+    for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+        int id = accessControlProfileIds[n];
+        uint32_t idBitMask = (1 << id);
+
+        // If the access control profile wasn't validated, this is an error and we
+        // fail immediately.
+        bool validated = ((ctx->accessControlProfileMaskValidated & idBitMask) != 0);
+        if (!validated) {
+            eicDebug("No ACP for profile id %d", id);
+            return EIC_ACCESS_CHECK_RESULT_FAILED;
+        }
+
+        // Otherwise, we _did_ validate the profile. If none of the checks
+        // failed, we're done
+        bool failedUserAuth = ((ctx->accessControlProfileMaskFailedUserAuth & idBitMask) != 0);
+        bool failedReaderAuth = ((ctx->accessControlProfileMaskFailedReaderAuth & idBitMask) != 0);
+        if (!failedUserAuth && !failedReaderAuth) {
+            result = EIC_ACCESS_CHECK_RESULT_OK;
+            break;
+        }
+        // One of the checks failed, convey which one
+        if (failedUserAuth) {
+            result = EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED;
+        } else {
+            result = EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED;
+        }
+    }
+    eicDebug("Result %d for name %s", result, name);
+
+    if (result == EIC_ACCESS_CHECK_RESULT_OK) {
+        eicCborAppendString(&ctx->cbor, name);
+        ctx->accessCheckOk = true;
+    }
+    return result;
+}
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+                                       size_t encryptedContentSize, uint8_t* content,
+                                       const char* nameSpace, const char* name,
+                                       const int* accessControlProfileIds,
+                                       size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+                                       size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        calculatedSha256)) {
+        return false;
+    }
+
+    if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+        eicDebug("SHA-256 mismatch of additionalData");
+        return false;
+    }
+    if (!ctx->accessCheckOk) {
+        eicDebug("Attempting to retrieve a value for which access is not granted");
+        return false;
+    }
+
+    if (!eicOpsDecryptAes128Gcm(ctx->storageKey, encryptedContent, encryptedContentSize,
+                                additionalDataCbor, additionalDataCborSize, content)) {
+        eicDebug("Error decrypting content");
+        return false;
+    }
+
+    eicCborAppend(&ctx->cbor, content, encryptedContentSize - 28);
+
+    return true;
+}
+
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+                                    size_t* digestToBeMacedSize) {
+    if (!ctx->buildCbor) {
+        *digestToBeMacedSize = 0;
+        return true;
+    }
+    if (*digestToBeMacedSize != 32) {
+        return false;
+    }
+
+    // This verifies that the correct expectedDeviceNamespacesSize value was
+    // passed in at eicPresentationCalcMacKey() time.
+    if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+        eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+        return false;
+    }
+    eicCborFinal(&ctx->cbor, digestToBeMaced);
+    return true;
+}
+
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+                                     const uint8_t* challenge, size_t challengeSize,
+                                     bool includeChallenge, size_t proofOfDeletionCborSize,
+                                     uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    EicCbor cbor;
+
+    eicCborInit(&cbor, NULL, 0);
+
+    // What we're going to sign is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    // Sig_structure = [
+    //   context : "Signature" / "Signature1" / "CounterSignature",
+    //   body_protected : empty_or_serialized_map,
+    //   ? sign_protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    //  ]
+    //
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfDeletionCborSize);
+
+    // Finally, the CBOR that we're actually signing.
+    eicCborAppendArray(&cbor, includeChallenge ? 4 : 3);
+    eicCborAppendString(&cbor, "ProofOfDeletion");
+    eicCborAppendString(&cbor, docType);
+    if (includeChallenge) {
+        eicCborAppendByteString(&cbor, challenge, challengeSize);
+    }
+    eicCborAppendBool(&cbor, ctx->testCredential);
+
+    uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, cborSha256);
+    if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+        eicDebug("Error signing proofOfDeletion");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+                                   const uint8_t* challenge, size_t challengeSize,
+                                   size_t proofOfOwnershipCborSize,
+                                   uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    EicCbor cbor;
+
+    eicCborInit(&cbor, NULL, 0);
+
+    // What we're going to sign is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    // Sig_structure = [
+    //   context : "Signature" / "Signature1" / "CounterSignature",
+    //   body_protected : empty_or_serialized_map,
+    //   ? sign_protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    //  ]
+    //
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborBegin(&cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, proofOfOwnershipCborSize);
+
+    // Finally, the CBOR that we're actually signing.
+    eicCborAppendArray(&cbor, 4);
+    eicCborAppendString(&cbor, "ProofOfOwnership");
+    eicCborAppendString(&cbor, docType);
+    eicCborAppendByteString(&cbor, challenge, challengeSize);
+    eicCborAppendBool(&cbor, testCredential);
+
+    uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+    eicCborFinal(&cbor, cborSha256);
+    if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+        eicDebug("Error signing proofOfDeletion");
+        return false;
+    }
+
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicPresentation.h b/identity/aidl/default/libeic/EicPresentation.h
new file mode 100644
index 0000000..c888049
--- /dev/null
+++ b/identity/aidl/default/libeic/EicPresentation.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+// The maximum size we support for public keys in reader certificates.
+#define EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE 65
+
+typedef struct {
+    int featureLevel;
+
+    uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+    uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    // The challenge generated with eicPresentationCreateAuthChallenge()
+    uint64_t authChallenge;
+
+    // Set by eicPresentationSetAuthToken() and contains the fields
+    // from the passed in authToken and verificationToken.
+    //
+    uint64_t authTokenChallenge;
+    uint64_t authTokenSecureUserId;
+    uint64_t authTokenTimestamp;
+    uint64_t verificationTokenTimestamp;
+
+    // The public key for the reader.
+    //
+    // (During the process of pushing reader certificates, this is also used to store
+    // the public key of the previously pushed certificate.)
+    //
+    uint8_t readerPublicKey[EIC_PRESENTATION_MAX_READER_PUBLIC_KEY_SIZE];
+    size_t readerPublicKeySize;
+
+    // This is set to true only if eicPresentationValidateRequestMessage() successfully
+    // validated the requestMessage.
+    //
+    // Why even record this? Because there's no requirement the HAL actually calls that
+    // function and we validate ACPs before it's called... so it's possible that a
+    // compromised HAL could trick us into marking ACPs as authorized while they in fact
+    // aren't.
+    bool requestMessageValidated;
+    bool buildCbor;
+
+    // Set to true initialized as a test credential.
+    bool testCredential;
+
+    // Set to true if the evaluation of access control checks in
+    // eicPresentationStartRetrieveEntryValue() resulted EIC_ACCESS_CHECK_RESULT_OK
+    bool accessCheckOk;
+
+    // These are bitmasks indicating which of the possible 32 access control profiles are
+    // authorized. They are built up by eicPresentationValidateAccessControlProfile().
+    //
+    uint32_t accessControlProfileMaskValidated;         // True if the profile was validated.
+    uint32_t accessControlProfileMaskUsesReaderAuth;    // True if the ACP is using reader auth
+    uint32_t accessControlProfileMaskFailedReaderAuth;  // True if failed reader auth
+    uint32_t accessControlProfileMaskFailedUserAuth;    // True if failed user auth
+
+    // SHA-256 for AdditionalData, updated for each entry.
+    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+    // SHA-256 of ProofOfProvisioning. Set to NUL-bytes or initialized from CredentialKeys data
+    // if credential was created with feature version 202101 or later.
+    uint8_t proofOfProvisioningSha256[EIC_SHA256_DIGEST_SIZE];
+
+    size_t expectedCborSizeAtEnd;
+    EicCbor cbor;
+} EicPresentation;
+
+bool eicPresentationInit(EicPresentation* ctx, bool testCredential, const char* docType,
+                         const uint8_t* encryptedCredentialKeys,
+                         size_t encryptedCredentialKeysSize);
+
+bool eicPresentationGenerateSigningKeyPair(EicPresentation* ctx, const char* docType, time_t now,
+                                           uint8_t* publicKeyCert, size_t* publicKeyCertSize,
+                                           uint8_t signingKeyBlob[60]);
+
+// Create an ephemeral key-pair.
+//
+// The private key is stored in |ctx->ephemeralPrivateKey| and also returned in
+// |ephemeralPrivateKey|.
+//
+bool eicPresentationCreateEphemeralKeyPair(EicPresentation* ctx,
+                                           uint8_t ephemeralPrivateKey[EIC_P256_PRIV_KEY_SIZE]);
+
+// Returns a non-zero challenge in |authChallenge|.
+bool eicPresentationCreateAuthChallenge(EicPresentation* ctx, uint64_t* authChallenge);
+
+// Starts retrieveing entries.
+//
+bool eicPresentationStartRetrieveEntries(EicPresentation* ctx);
+
+// Sets the auth-token.
+bool eicPresentationSetAuthToken(EicPresentation* ctx, uint64_t challenge, uint64_t secureUserId,
+                                 uint64_t authenticatorId, int hardwareAuthenticatorType,
+                                 uint64_t timeStamp, const uint8_t* mac, size_t macSize,
+                                 uint64_t verificationTokenChallenge,
+                                 uint64_t verificationTokenTimeStamp,
+                                 int verificationTokenSecurityLevel,
+                                 const uint8_t* verificationTokenMac,
+                                 size_t verificationTokenMacSize);
+
+// Function to push certificates in the reader certificate chain.
+//
+// This should start with the root certificate (e.g. the last in the chain) and
+// continue up the chain, ending with the certificate for the reader.
+//
+// Calls to this function should be interleaved with calls to the
+// eicPresentationValidateAccessControlProfile() function, see below.
+//
+bool eicPresentationPushReaderCert(EicPresentation* ctx, const uint8_t* certX509,
+                                   size_t certX509Size);
+
+// Checks an access control profile.
+//
+// Returns false if an error occurred while checking the profile (e.g. MAC doesn't check out).
+//
+// Returns in |accessGranted| whether access is granted.
+//
+// If |readerCertificate| is non-empty and the public key of one of those
+// certificates appear in the chain presented by the reader, this function must
+// be called after pushing that certificate using
+// eicPresentationPushReaderCert().
+//
+bool eicPresentationValidateAccessControlProfile(EicPresentation* ctx, int id,
+                                                 const uint8_t* readerCertificate,
+                                                 size_t readerCertificateSize,
+                                                 bool userAuthenticationRequired, int timeoutMillis,
+                                                 uint64_t secureUserId, const uint8_t mac[28],
+                                                 bool* accessGranted);
+
+// Validates that the given requestMessage is signed by the public key in the
+// certificate last set with eicPresentationPushReaderCert().
+//
+// The format of the signature is the same encoding as the 'signature' field of
+// COSE_Sign1 - that is, it's the R and S integers both with the same length as
+// the key-size.
+//
+// Must be called after eicPresentationPushReaderCert() have been used to push
+// the final certificate. Which is the certificate of the reader itself.
+//
+bool eicPresentationValidateRequestMessage(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                                           size_t sessionTranscriptSize,
+                                           const uint8_t* requestMessage, size_t requestMessageSize,
+                                           int coseSignAlg,
+                                           const uint8_t* readerSignatureOfToBeSigned,
+                                           size_t readerSignatureOfToBeSignedSize);
+
+typedef enum {
+    // Returned if access is granted.
+    EIC_ACCESS_CHECK_RESULT_OK,
+
+    // Returned if an error occurred checking for access.
+    EIC_ACCESS_CHECK_RESULT_FAILED,
+
+    // Returned if access was denied because item is configured without any
+    // access control profiles.
+    EIC_ACCESS_CHECK_RESULT_NO_ACCESS_CONTROL_PROFILES,
+
+    // Returned if access was denied because of user authentication.
+    EIC_ACCESS_CHECK_RESULT_USER_AUTHENTICATION_FAILED,
+
+    // Returned if access was denied because of reader authentication.
+    EIC_ACCESS_CHECK_RESULT_READER_AUTHENTICATION_FAILED,
+} EicAccessCheckResult;
+
+// Passes enough information to calculate the MACing key
+//
+bool eicPresentationCalcMacKey(EicPresentation* ctx, const uint8_t* sessionTranscript,
+                               size_t sessionTranscriptSize,
+                               const uint8_t readerEphemeralPublicKey[EIC_P256_PUB_KEY_SIZE],
+                               const uint8_t signingKeyBlob[60], const char* docType,
+                               unsigned int numNamespacesWithValues,
+                               size_t expectedDeviceNamespacesSize);
+
+// The scratchSpace should be set to a buffer at least 512 bytes (ideally 1024
+// bytes, the bigger the better). It's done this way to avoid allocating stack
+// space.
+//
+EicAccessCheckResult eicPresentationStartRetrieveEntryValue(
+        EicPresentation* ctx, const char* nameSpace, const char* name,
+        unsigned int newNamespaceNumEntries, int32_t entrySize, const int* accessControlProfileIds,
+        size_t numAccessControlProfileIds, uint8_t* scratchSpace, size_t scratchSpaceSize);
+
+// Note: |content| must be big enough to hold |encryptedContentSize| - 28 bytes.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicPresentationRetrieveEntryValue(EicPresentation* ctx, const uint8_t* encryptedContent,
+                                       size_t encryptedContentSize, uint8_t* content,
+                                       const char* nameSpace, const char* name,
+                                       const int* accessControlProfileIds,
+                                       size_t numAccessControlProfileIds, uint8_t* scratchSpace,
+                                       size_t scratchSpaceSize);
+
+// Returns the HMAC-SHA256 of |ToBeMaced| as per RFC 8051 "6.3. How to Compute
+// and Verify a MAC".
+bool eicPresentationFinishRetrieval(EicPresentation* ctx, uint8_t* digestToBeMaced,
+                                    size_t* digestToBeMacedSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfDeletion CBOR.
+//
+bool eicPresentationDeleteCredential(EicPresentation* ctx, const char* docType,
+                                     const uint8_t* challenge, size_t challengeSize,
+                                     bool includeChallenge, size_t proofOfDeletionCborSize,
+                                     uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfOwnership CBOR.
+//
+bool eicPresentationProveOwnership(EicPresentation* ctx, const char* docType, bool testCredential,
+                                   const uint8_t* challenge, size_t challengeSize,
+                                   size_t proofOfOwnershipCborSize,
+                                   uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_PRESENTATION_H
diff --git a/identity/aidl/default/libeic/EicProvisioning.c b/identity/aidl/default/libeic/EicProvisioning.c
new file mode 100644
index 0000000..3b4148e
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.c
@@ -0,0 +1,377 @@
+/*
+ * Copyright 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 "EicProvisioning.h"
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential) {
+    eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+    ctx->testCredential = testCredential;
+    if (!eicOpsRandom(ctx->storageKey, EIC_AES_128_KEY_SIZE)) {
+        return false;
+    }
+
+    return true;
+}
+
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+                                  const uint8_t* encryptedCredentialKeys,
+                                  size_t encryptedCredentialKeysSize) {
+    uint8_t credentialKeys[86];
+
+    // For feature version 202009 it's 52 bytes long and for feature version 202101 it's 86
+    // bytes (the additional data is the ProofOfProvisioning SHA-256). We need
+    // to support loading all feature versions.
+    //
+    bool expectPopSha256 = false;
+    if (encryptedCredentialKeysSize == 52 + 28) {
+        /* do nothing */
+    } else if (encryptedCredentialKeysSize == 86 + 28) {
+        expectPopSha256 = true;
+    } else {
+        eicDebug("Unexpected size %zd for encryptedCredentialKeys", encryptedCredentialKeysSize);
+        return false;
+    }
+
+    eicMemSet(ctx, '\0', sizeof(EicProvisioning));
+    ctx->testCredential = testCredential;
+
+    if (!eicOpsDecryptAes128Gcm(eicOpsGetHardwareBoundKey(testCredential), encryptedCredentialKeys,
+                                encryptedCredentialKeysSize,
+                                // DocType is the additionalAuthenticatedData
+                                (const uint8_t*)docType, eicStrLen(docType), credentialKeys)) {
+        eicDebug("Error decrypting CredentialKeys");
+        return false;
+    }
+
+    // It's supposed to look like this;
+    //
+    // Feature version 202009:
+    //
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
+    //         ]
+    //
+    // Feature version 202101:
+    //
+    //         CredentialKeys = [
+    //              bstr,   ; storageKey, a 128-bit AES key
+    //              bstr,   ; credentialPrivKey, the private key for credentialKey
+    //              bstr    ; proofOfProvisioning SHA-256
+    //         ]
+    //
+    // where storageKey is 16 bytes, credentialPrivateKey is 32 bytes, and proofOfProvisioning
+    // SHA-256 is 32 bytes.
+    //
+    if (credentialKeys[0] != (expectPopSha256 ? 0x83 : 0x82) ||  // array of two or three elements
+        credentialKeys[1] != 0x50 ||                             // 16-byte bstr
+        credentialKeys[18] != 0x58 || credentialKeys[19] != 0x20) {  // 32-byte bstr
+        eicDebug("Invalid CBOR for CredentialKeys");
+        return false;
+    }
+    if (expectPopSha256) {
+        if (credentialKeys[52] != 0x58 || credentialKeys[53] != 0x20) {  // 32-byte bstr
+            eicDebug("Invalid CBOR for CredentialKeys");
+            return false;
+        }
+    }
+    eicMemCpy(ctx->storageKey, credentialKeys + 2, EIC_AES_128_KEY_SIZE);
+    eicMemCpy(ctx->credentialPrivateKey, credentialKeys + 20, EIC_P256_PRIV_KEY_SIZE);
+    // Note: We don't care about the previous ProofOfProvisioning SHA-256
+    ctx->isUpdate = true;
+    return true;
+}
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+                                        size_t challengeSize, const uint8_t* applicationId,
+                                        size_t applicationIdSize, uint8_t* publicKeyCert,
+                                        size_t* publicKeyCertSize) {
+    if (ctx->isUpdate) {
+        eicDebug("Cannot create CredentialKey on update");
+        return false;
+    }
+
+    if (!eicOpsCreateCredentialKey(ctx->credentialPrivateKey, challenge, challengeSize,
+                                   applicationId, applicationIdSize, ctx->testCredential,
+                                   publicKeyCert, publicKeyCertSize)) {
+        return false;
+    }
+    return true;
+}
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+                                         const int* entryCounts, size_t numEntryCounts,
+                                         const char* docType,
+                                         size_t expectedProofOfProvisioningSize) {
+    if (numEntryCounts >= EIC_MAX_NUM_NAMESPACES) {
+        return false;
+    }
+    if (accessControlProfileCount >= EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS) {
+        return false;
+    }
+
+    ctx->numEntryCounts = numEntryCounts;
+    if (numEntryCounts > EIC_MAX_NUM_NAMESPACES) {
+        return false;
+    }
+    for (size_t n = 0; n < numEntryCounts; n++) {
+        if (entryCounts[n] >= 256) {
+            return false;
+        }
+        ctx->entryCounts[n] = entryCounts[n];
+    }
+    ctx->curNamespace = -1;
+    ctx->curNamespaceNumProcessed = 0;
+
+    eicCborInit(&ctx->cbor, NULL, 0);
+
+    // What we're going to sign is the COSE ToBeSigned structure which
+    // looks like the following:
+    //
+    // Sig_structure = [
+    //   context : "Signature" / "Signature1" / "CounterSignature",
+    //   body_protected : empty_or_serialized_map,
+    //   ? sign_protected : empty_or_serialized_map,
+    //   external_aad : bstr,
+    //   payload : bstr
+    //  ]
+    //
+    eicCborAppendArray(&ctx->cbor, 4);
+    eicCborAppendString(&ctx->cbor, "Signature1");
+
+    // The COSE Encoded protected headers is just a single field with
+    // COSE_LABEL_ALG (1) -> COSE_ALG_ECSDA_256 (-7). For simplicitly we just
+    // hard-code the CBOR encoding:
+    static const uint8_t coseEncodedProtectedHeaders[] = {0xa1, 0x01, 0x26};
+    eicCborAppendByteString(&ctx->cbor, coseEncodedProtectedHeaders,
+                            sizeof(coseEncodedProtectedHeaders));
+
+    // We currently don't support Externally Supplied Data (RFC 8152 section 4.3)
+    // so external_aad is the empty bstr
+    static const uint8_t externalAad[0] = {};
+    eicCborAppendByteString(&ctx->cbor, externalAad, sizeof(externalAad));
+
+    // For the payload, the _encoded_ form follows here. We handle this by simply
+    // opening a bstr, and then writing the CBOR. This requires us to know the
+    // size of said bstr, ahead of time.
+    eicCborBegin(&ctx->cbor, EIC_CBOR_MAJOR_TYPE_BYTE_STRING, expectedProofOfProvisioningSize);
+    ctx->expectedCborSizeAtEnd = expectedProofOfProvisioningSize + ctx->cbor.size;
+
+    eicOpsSha256Init(&ctx->proofOfProvisioningDigester);
+    eicCborEnableSecondaryDigesterSha256(&ctx->cbor, &ctx->proofOfProvisioningDigester);
+
+    eicCborAppendArray(&ctx->cbor, 5);
+    eicCborAppendString(&ctx->cbor, "ProofOfProvisioning");
+    eicCborAppendString(&ctx->cbor, docType);
+
+    eicCborAppendArray(&ctx->cbor, accessControlProfileCount);
+
+    return true;
+}
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+                                            const uint8_t* readerCertificate,
+                                            size_t readerCertificateSize,
+                                            bool userAuthenticationRequired, uint64_t timeoutMillis,
+                                            uint64_t secureUserId, uint8_t outMac[28]) {
+    uint8_t cborBuffer[EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE];
+    EicCbor cborBuilder;
+
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis, secureUserId)) {
+        return false;
+    }
+
+    // Calculate and return MAC
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, NULL, 0, cborBuilder.buffer,
+                                cborBuilder.size, outMac)) {
+        return false;
+    }
+
+    // The ACP CBOR in the provisioning receipt doesn't include secureUserId so build
+    // it again.
+    eicCborInit(&cborBuilder, cborBuffer, EIC_MAX_CBOR_SIZE_FOR_ACCESS_CONTROL_PROFILE);
+    if (!eicCborCalcAccessControl(&cborBuilder, id, readerCertificate, readerCertificateSize,
+                                  userAuthenticationRequired, timeoutMillis,
+                                  0 /* secureUserId */)) {
+        return false;
+    }
+
+    // Append the CBOR from the local builder to the digester.
+    eicCborAppend(&ctx->cbor, cborBuilder.buffer, cborBuilder.size);
+
+    return true;
+}
+
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    // We'll need to calc and store a digest of additionalData to check that it's the same
+    // additionalData being passed in for every eicProvisioningAddEntryValue() call...
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        ctx->additionalDataSha256)) {
+        return false;
+    }
+
+    if (ctx->curNamespace == -1) {
+        ctx->curNamespace = 0;
+        ctx->curNamespaceNumProcessed = 0;
+        // Opens the main map: { * Namespace => [ + Entry ] }
+        eicCborAppendMap(&ctx->cbor, ctx->numEntryCounts);
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        // Opens the per-namespace array: [ + Entry ]
+        eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+    }
+
+    if (ctx->curNamespaceNumProcessed == ctx->entryCounts[ctx->curNamespace]) {
+        ctx->curNamespace += 1;
+        ctx->curNamespaceNumProcessed = 0;
+        eicCborAppendString(&ctx->cbor, nameSpace);
+        // Opens the per-namespace array: [ + Entry ]
+        eicCborAppendArray(&ctx->cbor, ctx->entryCounts[ctx->curNamespace]);
+    }
+
+    eicCborAppendMap(&ctx->cbor, 3);
+    eicCborAppendString(&ctx->cbor, "name");
+    eicCborAppendString(&ctx->cbor, name);
+
+    ctx->curEntrySize = entrySize;
+    ctx->curEntryNumBytesReceived = 0;
+
+    eicCborAppendString(&ctx->cbor, "value");
+
+    ctx->curNamespaceNumProcessed += 1;
+    return true;
+}
+
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, const uint8_t* content, size_t contentSize,
+                                  uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize) {
+    uint8_t* additionalDataCbor = scratchSpace;
+    const size_t additionalDataCborBufSize = scratchSpaceSize;
+    size_t additionalDataCborSize;
+
+    uint8_t calculatedSha256[EIC_SHA256_DIGEST_SIZE];
+    if (!eicCborCalcEntryAdditionalData(accessControlProfileIds, numAccessControlProfileIds,
+                                        nameSpace, name, additionalDataCbor,
+                                        additionalDataCborBufSize, &additionalDataCborSize,
+                                        calculatedSha256)) {
+        return false;
+    }
+    if (eicCryptoMemCmp(calculatedSha256, ctx->additionalDataSha256, EIC_SHA256_DIGEST_SIZE) != 0) {
+        eicDebug("SHA-256 mismatch of additionalData");
+        return false;
+    }
+
+    eicCborAppend(&ctx->cbor, content, contentSize);
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(ctx->storageKey, nonce, content, contentSize, additionalDataCbor,
+                                additionalDataCborSize, outEncryptedContent)) {
+        return false;
+    }
+
+    // If done with this entry, close the map
+    ctx->curEntryNumBytesReceived += contentSize;
+    if (ctx->curEntryNumBytesReceived == ctx->curEntrySize) {
+        eicCborAppendString(&ctx->cbor, "accessControlProfiles");
+        eicCborAppendArray(&ctx->cbor, numAccessControlProfileIds);
+        for (size_t n = 0; n < numAccessControlProfileIds; n++) {
+            eicCborAppendNumber(&ctx->cbor, accessControlProfileIds[n]);
+        }
+    }
+    return true;
+}
+
+bool eicProvisioningFinishAddingEntries(
+        EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]) {
+    uint8_t cborSha256[EIC_SHA256_DIGEST_SIZE];
+
+    eicCborAppendBool(&ctx->cbor, ctx->testCredential);
+    eicCborFinal(&ctx->cbor, cborSha256);
+
+    // This verifies that the correct expectedProofOfProvisioningSize value was
+    // passed in at eicStartPersonalization() time.
+    if (ctx->cbor.size != ctx->expectedCborSizeAtEnd) {
+        eicDebug("CBOR size is %zd, was expecting %zd", ctx->cbor.size, ctx->expectedCborSizeAtEnd);
+        return false;
+    }
+
+    if (!eicOpsEcDsa(ctx->credentialPrivateKey, cborSha256, signatureOfToBeSigned)) {
+        eicDebug("Error signing proofOfProvisioning");
+        return false;
+    }
+
+    return true;
+}
+
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+                                            uint8_t* encryptedCredentialKeys,
+                                            size_t* encryptedCredentialKeysSize) {
+    EicCbor cbor;
+    uint8_t cborBuf[86];
+
+    if (*encryptedCredentialKeysSize < 86 + 28) {
+        eicDebug("encryptedCredentialKeysSize is %zd which is insufficient");
+        return false;
+    }
+
+    eicCborInit(&cbor, cborBuf, sizeof(cborBuf));
+    eicCborAppendArray(&cbor, 3);
+    eicCborAppendByteString(&cbor, ctx->storageKey, EIC_AES_128_KEY_SIZE);
+    eicCborAppendByteString(&cbor, ctx->credentialPrivateKey, EIC_P256_PRIV_KEY_SIZE);
+    uint8_t popSha256[EIC_SHA256_DIGEST_SIZE];
+    eicOpsSha256Final(&ctx->proofOfProvisioningDigester, popSha256);
+    eicCborAppendByteString(&cbor, popSha256, EIC_SHA256_DIGEST_SIZE);
+    if (cbor.size > sizeof(cborBuf)) {
+        eicDebug("Exceeded buffer size");
+        return false;
+    }
+
+    uint8_t nonce[12];
+    if (!eicOpsRandom(nonce, 12)) {
+        eicDebug("Error getting random");
+        return false;
+    }
+    if (!eicOpsEncryptAes128Gcm(
+                eicOpsGetHardwareBoundKey(ctx->testCredential), nonce, cborBuf, cbor.size,
+                // DocType is the additionalAuthenticatedData
+                (const uint8_t*)docType, eicStrLen(docType), encryptedCredentialKeys)) {
+        eicDebug("Error encrypting CredentialKeys");
+        return false;
+    }
+    *encryptedCredentialKeysSize = cbor.size + 28;
+
+    return true;
+}
diff --git a/identity/aidl/default/libeic/EicProvisioning.h b/identity/aidl/default/libeic/EicProvisioning.h
new file mode 100644
index 0000000..f064787
--- /dev/null
+++ b/identity/aidl/default/libeic/EicProvisioning.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright 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.
+ */
+
+#if !defined(EIC_INSIDE_LIBEIC_H) && !defined(EIC_COMPILATION)
+#error "Never include this file directly, include libeic.h instead."
+#endif
+
+#ifndef ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+#define ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "EicCbor.h"
+
+#define EIC_MAX_NUM_NAMESPACES 32
+#define EIC_MAX_NUM_ACCESS_CONTROL_PROFILE_IDS 32
+
+typedef struct {
+    // Set by eicCreateCredentialKey() OR eicProvisioningInitForUpdate()
+    uint8_t credentialPrivateKey[EIC_P256_PRIV_KEY_SIZE];
+
+    int numEntryCounts;
+    uint8_t entryCounts[EIC_MAX_NUM_NAMESPACES];
+
+    int curNamespace;
+    int curNamespaceNumProcessed;
+
+    size_t curEntrySize;
+    size_t curEntryNumBytesReceived;
+
+    // Set by eicProvisioningInit() OR eicProvisioningInitForUpdate()
+    uint8_t storageKey[EIC_AES_128_KEY_SIZE];
+
+    size_t expectedCborSizeAtEnd;
+
+    // SHA-256 for AdditionalData, updated for each entry.
+    uint8_t additionalDataSha256[EIC_SHA256_DIGEST_SIZE];
+
+    // Digester just for ProofOfProvisioning (without Sig_structure).
+    EicSha256Ctx proofOfProvisioningDigester;
+
+    EicCbor cbor;
+
+    bool testCredential;
+
+    // Set to true if this is an update.
+    bool isUpdate;
+} EicProvisioning;
+
+bool eicProvisioningInit(EicProvisioning* ctx, bool testCredential);
+
+bool eicProvisioningInitForUpdate(EicProvisioning* ctx, bool testCredential, const char* docType,
+                                  const uint8_t* encryptedCredentialKeys,
+                                  size_t encryptedCredentialKeysSize);
+
+bool eicProvisioningCreateCredentialKey(EicProvisioning* ctx, const uint8_t* challenge,
+                                        size_t challengeSize, const uint8_t* applicationId,
+                                        size_t applicationIdSize, uint8_t* publicKeyCert,
+                                        size_t* publicKeyCertSize);
+
+bool eicProvisioningStartPersonalization(EicProvisioning* ctx, int accessControlProfileCount,
+                                         const int* entryCounts, size_t numEntryCounts,
+                                         const char* docType,
+                                         size_t expectedProofOfProvisioningingSize);
+
+bool eicProvisioningAddAccessControlProfile(EicProvisioning* ctx, int id,
+                                            const uint8_t* readerCertificate,
+                                            size_t readerCertificateSize,
+                                            bool userAuthenticationRequired, uint64_t timeoutMillis,
+                                            uint64_t secureUserId, uint8_t outMac[28]);
+
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningBeginAddEntry(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, uint64_t entrySize, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize);
+
+// The outEncryptedContent array must be contentSize + 28 bytes long.
+//
+// The scratchSpace should be set to a buffer at least 512 bytes. It's done this way to
+// avoid allocating stack space.
+//
+bool eicProvisioningAddEntryValue(EicProvisioning* ctx, const int* accessControlProfileIds,
+                                  size_t numAccessControlProfileIds, const char* nameSpace,
+                                  const char* name, const uint8_t* content, size_t contentSize,
+                                  uint8_t* outEncryptedContent, uint8_t* scratchSpace,
+                                  size_t scratchSpaceSize);
+
+// The data returned in |signatureOfToBeSigned| contains the ECDSA signature of
+// the ToBeSigned CBOR from RFC 8051 "4.4. Signing and Verification Process"
+// where content is set to the ProofOfProvisioninging CBOR.
+//
+bool eicProvisioningFinishAddingEntries(
+        EicProvisioning* ctx, uint8_t signatureOfToBeSigned[EIC_ECDSA_P256_SIGNATURE_SIZE]);
+
+//
+//
+// The |encryptedCredentialKeys| array is set to AES-GCM-ENC(HBK, R, CredentialKeys, docType)
+// where
+//
+//   CredentialKeys = [
+//     bstr,   ; storageKey, a 128-bit AES key
+//     bstr    ; credentialPrivKey, the private key for credentialKey
+//     bstr    ; SHA-256(ProofOfProvisioning)
+//   ]
+//
+// for feature version 202101. For feature version 202009 the third field was not present.
+//
+// Since |storageKey| is 16 bytes and |credentialPrivKey| is 32 bytes, the
+// encoded CBOR for CredentialKeys is 86 bytes and consequently
+// |encryptedCredentialKeys| will be no longer than 86 + 28 = 114 bytes.
+//
+bool eicProvisioningFinishGetCredentialData(EicProvisioning* ctx, const char* docType,
+                                            uint8_t* encryptedCredentialKeys,
+                                            size_t* encryptedCredentialKeysSize);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_EIC_PROVISIONING_H
diff --git a/identity/aidl/default/libeic/libeic.h b/identity/aidl/default/libeic/libeic.h
new file mode 100644
index 0000000..88abef8
--- /dev/null
+++ b/identity/aidl/default/libeic/libeic.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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_IDENTITY_LIBEIC_H
+#define ANDROID_HARDWARE_IDENTITY_LIBEIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The EIC_INSIDE_LIBEIC_H preprocessor symbol is used to enforce
+ * library users to include only this file. All public interfaces, and
+ * only public interfaces, must be included here.
+ */
+#define EIC_INSIDE_LIBEIC_H
+#include "EicCbor.h"
+#include "EicOps.h"
+#include "EicPresentation.h"
+#include "EicProvisioning.h"
+#undef EIC_INSIDE_LIBEIC_H
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif  // ANDROID_HARDWARE_IDENTITY_LIBEIC_H
diff --git a/identity/aidl/default/service.cpp b/identity/aidl/default/service.cpp
index bf95df5..c290c08 100644
--- a/identity/aidl/default/service.cpp
+++ b/identity/aidl/default/service.cpp
@@ -22,20 +22,26 @@
 
 #include "IdentityCredentialStore.h"
 
+#include "FakeSecureHardwareProxy.h"
+
+using ::android::sp;
 using ::android::base::InitLogging;
 using ::android::base::StderrLogger;
 
-using aidl::android::hardware::identity::IdentityCredentialStore;
+using ::aidl::android::hardware::identity::IdentityCredentialStore;
+using ::android::hardware::identity::FakeSecureHardwareProxyFactory;
+using ::android::hardware::identity::SecureHardwareProxyFactory;
 
 int main(int /*argc*/, char* argv[]) {
     InitLogging(argv, StderrLogger);
 
+    sp<SecureHardwareProxyFactory> hwProxyFactory = new FakeSecureHardwareProxyFactory();
+
     ABinderProcess_setThreadPoolMaxThreadCount(0);
     std::shared_ptr<IdentityCredentialStore> store =
-            ndk::SharedRefBase::make<IdentityCredentialStore>();
+            ndk::SharedRefBase::make<IdentityCredentialStore>(hwProxyFactory);
 
     const std::string instance = std::string() + IdentityCredentialStore::descriptor + "/default";
-    LOG(INFO) << "instance: " << instance;
     binder_status_t status = AServiceManager_addService(store->asBinder().get(), instance.c_str());
     CHECK(status == STATUS_OK);
 
diff --git a/identity/aidl/vts/Android.bp b/identity/aidl/vts/Android.bp
index 03966de..61d15d2 100644
--- a/identity/aidl/vts/Android.bp
+++ b/identity/aidl/vts/Android.bp
@@ -1,30 +1,48 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalIdentityTargetTest",
     defaults: [
         "VtsHalTargetTestDefaults",
         "use_libaidlvintf_gtest_helper_static",
     ],
+    cflags: [
+        "-Wno-deprecated-declarations",
+    ],
     srcs: [
-        "VtsHalIdentityEndToEndTest.cpp",
         "VtsIWritableIdentityCredentialTests.cpp",
-        "VtsIdentityTestUtils.cpp",
+        "Util.cpp",
         "VtsAttestationTests.cpp",
         "UserAuthTests.cpp",
         "ReaderAuthTests.cpp",
+        "DeleteCredentialTests.cpp",
+        "ProveOwnershipTests.cpp",
+        "UpdateCredentialTests.cpp",
+        "EndToEndTests.cpp",
+        "TestCredentialTests.cpp",
+        "AuthenticationKeyTests.cpp",
     ],
     shared_libs: [
         "libbinder",
         "libcrypto",
     ],
     static_libs: [
-        "libcppbor",
+        "libcppbor_external",
+        "libcppcose_rkp",
         "libkeymaster_portable",
         "libpuresoftkeymasterdevice",
         "android.hardware.keymaster@4.0",
         "android.hardware.identity-support-lib",
-        "android.hardware.identity-cpp",
-        "android.hardware.keymaster-cpp",
-        "android.hardware.keymaster-ndk_platform",
+        "android.hardware.identity-V3-cpp",
+        "android.hardware.keymaster-V3-cpp",
+        "android.hardware.keymaster-V3-ndk_platform",
         "libkeymaster4support",
         "libkeymaster4_1support",
     ],
diff --git a/identity/aidl/vts/AuthenticationKeyTests.cpp b/identity/aidl/vts/AuthenticationKeyTests.cpp
new file mode 100644
index 0000000..25d74d4
--- /dev/null
+++ b/identity/aidl/vts/AuthenticationKeyTests.cpp
@@ -0,0 +1,194 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class AuthenticationKeyTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        string halInstanceName = GetParam();
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(halInstanceName.c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+TEST_P(AuthenticationKeyTests, proofOfProvisionInAuthKeyCert) {
+    if (halApiVersion_ < 3) {
+        GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+    }
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_
+                        ->createCredential(docType,
+                                           true,  // testCredential
+                                           &wc)
+                        .isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    optional<vector<uint8_t>> optCredentialPubKey =
+            support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+    ASSERT_TRUE(optCredentialPubKey);
+    vector<uint8_t> credentialPubKey;
+    credentialPubKey = optCredentialPubKey.value();
+
+    size_t proofOfProvisioningSize = 112;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    vector<uint8_t> encryptedData;
+    vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+    ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    vector<uint8_t> credentialData;
+    Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'ns' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+    // Make sure it's signed by the CredentialKey in the returned cert chain.
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey));
+
+    // Now get a credential and have it create AuthenticationKey so we can check
+    // the certificate.
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData, &credential)
+                        .isOk());
+    vector<uint8_t> signingKeyBlob;
+    Certificate signingKeyCertificate;
+    ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+    optional<vector<uint8_t>> signingPubKey =
+            support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+    EXPECT_TRUE(signingPubKey);
+
+    // SHA-256(ProofOfProvisioning) is embedded in CBOR with the following CDDL
+    //
+    //   ProofOfBinding = [
+    //     "ProofOfBinding",
+    //     bstr,                  // Contains the SHA-256 of ProofOfProvisioning
+    //   ]
+    //
+    // Check that.
+    //
+    optional<vector<uint8_t>> proofOfBinding = support::certificateGetExtension(
+            signingKeyCertificate.encodedCertificate, "1.3.6.1.4.1.11129.2.1.26");
+    ASSERT_TRUE(proofOfBinding);
+    auto [item, _, message] = cppbor::parse(proofOfBinding.value());
+    ASSERT_NE(item, nullptr) << message;
+    const cppbor::Array* arrayItem = item->asArray();
+    ASSERT_NE(arrayItem, nullptr);
+    ASSERT_EQ(arrayItem->size(), 2);
+    const cppbor::Tstr* strItem = (*arrayItem)[0]->asTstr();
+    ASSERT_NE(strItem, nullptr);
+    EXPECT_EQ(strItem->value(), "ProofOfBinding");
+    const cppbor::Bstr* popSha256Item = (*arrayItem)[1]->asBstr();
+    ASSERT_NE(popSha256Item, nullptr);
+    EXPECT_EQ(popSha256Item->value(), support::sha256(proofOfProvisioning.value()));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AuthenticationKeyTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, AuthenticationKeyTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/DeleteCredentialTests.cpp b/identity/aidl/vts/DeleteCredentialTests.cpp
new file mode 100644
index 0000000..7627c9c
--- /dev/null
+++ b/identity/aidl/vts/DeleteCredentialTests.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "DeleteCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class DeleteCredentialTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    void provisionData();
+
+    // Set by provisionData
+    vector<uint8_t> credentialData_;
+    vector<uint8_t> credentialPubKey_;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+void DeleteCredentialTests::provisionData() {
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    optional<vector<uint8_t>> optCredentialPubKey =
+            support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+    ASSERT_TRUE(optCredentialPubKey);
+    credentialPubKey_ = optCredentialPubKey.value();
+
+    size_t proofOfProvisioningSize = 106;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Some Data", 1).isOk());
+    vector<uint8_t> encryptedData;
+    ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(DeleteCredentialTests, Delete) {
+    provisionData();
+
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &credential)
+                        .isOk());
+
+    vector<uint8_t> proofOfDeletionSignature;
+    ASSERT_TRUE(credential->deleteCredential(&proofOfDeletionSignature).isOk());
+    optional<vector<uint8_t>> proofOfDeletion =
+            support::coseSignGetPayload(proofOfDeletionSignature);
+    ASSERT_TRUE(proofOfDeletion);
+    string cborPretty = cppbor::prettyPrint(proofOfDeletion.value(), 32, {});
+    EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', true, ]", cborPretty);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {},  // Additional data
+                                                 credentialPubKey_));
+}
+
+TEST_P(DeleteCredentialTests, DeleteWithChallenge) {
+    if (halApiVersion_ < 3) {
+        GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+    }
+
+    provisionData();
+
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &credential)
+                        .isOk());
+
+    vector<uint8_t> challenge = {65, 66, 67};
+    vector<uint8_t> proofOfDeletionSignature;
+    ASSERT_TRUE(
+            credential->deleteCredentialWithChallenge(challenge, &proofOfDeletionSignature).isOk());
+    optional<vector<uint8_t>> proofOfDeletion =
+            support::coseSignGetPayload(proofOfDeletionSignature);
+    ASSERT_TRUE(proofOfDeletion);
+    string cborPretty = cppbor::prettyPrint(proofOfDeletion.value(), 32, {});
+    EXPECT_EQ("['ProofOfDeletion', 'org.iso.18013-5.2019.mdl', {0x41, 0x42, 0x43}, true, ]",
+              cborPretty);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfDeletionSignature, {},  // Additional data
+                                                 credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(DeleteCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, DeleteCredentialTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/EndToEndTests.cpp b/identity/aidl/vts/EndToEndTests.cpp
new file mode 100644
index 0000000..67db915
--- /dev/null
+++ b/identity/aidl/vts/EndToEndTests.cpp
@@ -0,0 +1,536 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+#define LOG_TAG "VtsHalIdentityEndToEndTest"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <tuple>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_tuple;
+using std::map;
+using std::optional;
+using std::string;
+using std::tuple;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+using test_utils::validateAttestationCertificate;
+
+class EndToEndTests : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+TEST_P(EndToEndTests, hardwareInformation) {
+    HardwareInformation info;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
+    ASSERT_GT(info.credentialStoreName.size(), 0);
+    ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
+    ASSERT_GE(info.dataChunkSize, 256);
+}
+
+tuple<bool, string, vector<uint8_t>, vector<uint8_t>, vector<uint8_t>>
+extractFromTestCredentialData(const vector<uint8_t>& credentialData) {
+    string docType;
+    vector<uint8_t> storageKey;
+    vector<uint8_t> credentialPrivKey;
+    vector<uint8_t> sha256Pop;
+
+    auto [item, _, message] = cppbor::parse(credentialData);
+    if (item == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+
+    const cppbor::Array* arrayItem = item->asArray();
+    if (arrayItem == nullptr || arrayItem->size() != 3) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+
+    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+    const cppbor::Bool* testCredentialItem =
+            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+                                                    : nullptr);
+    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
+        encryptedCredentialKeysItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+
+    docType = docTypeItem->value();
+
+    vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+    const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+    optional<vector<uint8_t>> decryptedCredentialKeys =
+            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+    if (!decryptedCredentialKeys) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+
+    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+    if (dckItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+    const cppbor::Array* dckArrayItem = dckItem->asArray();
+    if (dckArrayItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+    if (dckArrayItem->size() < 2) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
+        return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+    }
+    storageKey = storageKeyItem->value();
+    credentialPrivKey = credentialPrivKeyItem->value();
+    if (dckArrayItem->size() == 3) {
+        const cppbor::Bstr* sha256PopItem = (*dckArrayItem)[2]->asBstr();
+        if (sha256PopItem == nullptr) {
+            return make_tuple(false, docType, storageKey, credentialPrivKey, sha256Pop);
+        }
+        sha256Pop = sha256PopItem->value();
+    }
+    return make_tuple(true, docType, storageKey, credentialPrivKey, sha256Pop);
+}
+
+TEST_P(EndToEndTests, createAndRetrieveCredential) {
+    // First, generate a key-pair for the reader since its public key will be
+    // part of the request data.
+    vector<uint8_t> readerKey;
+    optional<vector<uint8_t>> readerCertificate =
+            test_utils::generateReaderCertificate("1234", &readerKey);
+    ASSERT_TRUE(readerCertificate);
+
+    // Make the portrait image really big (just shy of 256 KiB) to ensure that
+    // the chunking code gets exercised.
+    vector<uint8_t> portraitImage;
+    test_utils::setImageData(portraitImage);
+
+    // Access control profiles:
+    const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
+                                                          {0, readerCertificate.value(), false, 0},
+                                                          // Profile 1 (no authentication)
+                                                          {1, {}, false, 0}};
+
+    // It doesn't matter since no user auth is needed in this particular test,
+    // but for good measure, clear out the tokens we pass to the HAL.
+    HardwareAuthToken authToken;
+    VerificationToken verificationToken;
+    authToken.challenge = 0;
+    authToken.userId = 0;
+    authToken.authenticatorId = 0;
+    authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
+    authToken.timestamp.milliSeconds = 0;
+    authToken.mac.clear();
+    verificationToken.challenge = 0;
+    verificationToken.timestamp.milliSeconds = 0;
+    verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
+    verificationToken.mac.clear();
+
+    // Here's the actual test data:
+    const vector<test_utils::TestEntryData> testEntries = {
+            {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
+            {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
+            {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
+            {"PersonalData", "Home address", string("Maida Vale, London, England"),
+             vector<int32_t>{0}},
+            {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
+    };
+    const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
+                                                    1u};
+    HardwareInformation hwInfo;
+    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
+
+    string cborPretty;
+    sp<IWritableIdentityCredential> writableCredential;
+    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
+                                                    true /* testCredential */));
+
+    string challenge = "attestationChallenge";
+    test_utils::AttestationData attData(writableCredential, challenge,
+                                        {1} /* atteestationApplicationId */);
+    ASSERT_TRUE(attData.result.isOk())
+            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
+
+    validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
+                                   attData.attestationApplicationId, true);
+
+    // This is kinda of a hack but we need to give the size of
+    // ProofOfProvisioning that we'll expect to receive.
+    const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
+    // OK to fail, not available in v1 HAL
+    writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
+    ASSERT_TRUE(
+            writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
+                    .isOk());
+
+    optional<vector<SecureAccessControlProfile>> secureProfiles =
+            test_utils::addAccessControlProfiles(writableCredential, testProfiles);
+    ASSERT_TRUE(secureProfiles);
+
+    // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
+    // is a little hacky but it works well enough.
+    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
+
+    for (const auto& entry : testEntries) {
+        ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
+                                         encryptedBlobs, true));
+    }
+
+    vector<uint8_t> credentialData;
+    vector<uint8_t> proofOfProvisioningSignature;
+    ASSERT_TRUE(
+            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
+                    .isOk());
+
+    // Validate the proofOfProvisioning which was returned
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 0,\n"
+            "      'readerCertificate' : <not printed>,\n"
+            "    },\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'PersonalData' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Birth date',\n"
+            "        'value' : '19120623',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'First name',\n"
+            "        'value' : 'Alan',\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "      {\n"
+            "        'name' : 'Home address',\n"
+            "        'value' : 'Maida Vale, London, England',\n"
+            "        'accessControlProfiles' : [0, ],\n"
+            "      },\n"
+            "    ],\n"
+            "    'Image' : [\n"
+            "      {\n"
+            "        'name' : 'Portrait image',\n"
+            "        'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "        'accessControlProfiles' : [0, 1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+
+    optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
+            attData.attestationCertificate[0].encodedCertificate);
+    ASSERT_TRUE(credentialPubKey);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey.value()));
+    writableCredential = nullptr;
+
+    // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
+    // only because we asked for a test-credential meaning that the HBK is all zeroes.
+    auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey, exSha256Pop] =
+            extractFromTestCredentialData(credentialData);
+
+    ASSERT_TRUE(exSuccess);
+    ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
+    // ... check that the public key derived from the private key matches what was
+    // in the certificate.
+    optional<vector<uint8_t>> exCredentialKeyPair =
+            support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
+    ASSERT_TRUE(exCredentialKeyPair);
+    optional<vector<uint8_t>> exCredentialPubKey =
+            support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
+    ASSERT_TRUE(exCredentialPubKey);
+    ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
+
+    // Starting with API version 3 (feature version 202101) we require SHA-256(ProofOfProvisioning)
+    // to be in CredentialKeys (which is stored encrypted in CredentialData). Check
+    // that it's there with the expected value.
+    if (halApiVersion_ >= 3) {
+        ASSERT_EQ(exSha256Pop, support::sha256(proofOfProvisioning.value()));
+    }
+
+    // Now that the credential has been provisioned, read it back and check the
+    // correct data is returned.
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData, &credential)
+                        .isOk());
+    ASSERT_NE(credential, nullptr);
+
+    optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
+    ASSERT_TRUE(readerEphemeralKeyPair);
+    optional<vector<uint8_t>> readerEphemeralPublicKey =
+            support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
+    ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
+
+    vector<uint8_t> ephemeralKeyPair;
+    ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
+    optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
+
+    // Calculate requestData field and sign it with the reader key.
+    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
+    ASSERT_TRUE(getXYSuccess);
+    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
+    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
+    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
+    cppbor::Array sessionTranscript = cppbor::Array()
+                                              .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+                                              .add(cppbor::SemanticTag(24, eReaderPubBytes));
+    vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
+
+    vector<uint8_t> itemsRequestBytes =
+            cppbor::Map("nameSpaces",
+                        cppbor::Map()
+                                .add("PersonalData", cppbor::Map()
+                                                             .add("Last name", false)
+                                                             .add("Birth date", false)
+                                                             .add("First name", false)
+                                                             .add("Home address", true))
+                                .add("Image", cppbor::Map().add("Portrait image", false)))
+                    .encode();
+    cborPretty = cppbor::prettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
+    EXPECT_EQ(
+            "{\n"
+            "  'nameSpaces' : {\n"
+            "    'PersonalData' : {\n"
+            "      'Last name' : false,\n"
+            "      'Birth date' : false,\n"
+            "      'First name' : false,\n"
+            "      'Home address' : true,\n"
+            "    },\n"
+            "    'Image' : {\n"
+            "      'Portrait image' : false,\n"
+            "    },\n"
+            "  },\n"
+            "}",
+            cborPretty);
+    vector<uint8_t> encodedReaderAuthentication =
+            cppbor::Array()
+                    .add("ReaderAuthentication")
+                    .add(sessionTranscript.clone())
+                    .add(cppbor::SemanticTag(24, itemsRequestBytes))
+                    .encode();
+    vector<uint8_t> encodedReaderAuthenticationBytes =
+            cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
+    optional<vector<uint8_t>> readerSignature =
+            support::coseSignEcDsa(readerKey, {},                     // content
+                                   encodedReaderAuthenticationBytes,  // detached content
+                                   readerCertificate.value());
+    ASSERT_TRUE(readerSignature);
+
+    // Generate the key that will be used to sign AuthenticatedData.
+    vector<uint8_t> signingKeyBlob;
+    Certificate signingKeyCertificate;
+    ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
+    optional<vector<uint8_t>> signingPubKey =
+            support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
+    EXPECT_TRUE(signingPubKey);
+    test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
+
+    // Since we're using a test-credential we know storageKey meaning we can get the
+    // private key. Do this, derive the public key from it, and check this matches what
+    // is in the certificate...
+    const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
+    optional<vector<uint8_t>> exSigningPrivKey =
+            support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
+    ASSERT_TRUE(exSigningPrivKey);
+    optional<vector<uint8_t>> exSigningKeyPair =
+            support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
+    ASSERT_TRUE(exSigningKeyPair);
+    optional<vector<uint8_t>> exSigningPubKey =
+            support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
+    ASSERT_TRUE(exSigningPubKey);
+    ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
+
+    vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
+    // OK to fail, not available in v1 HAL
+    credential->setRequestedNamespaces(requestedNamespaces);
+    // OK to fail, not available in v1 HAL
+    credential->setVerificationToken(verificationToken);
+    ASSERT_TRUE(credential
+                        ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
+                                         signingKeyBlob, sessionTranscriptEncoded,
+                                         readerSignature.value(), testEntriesEntryCounts)
+                        .isOk());
+
+    for (const auto& entry : testEntries) {
+        ASSERT_TRUE(credential
+                            ->startRetrieveEntryValue(entry.nameSpace, entry.name,
+                                                      entry.valueCbor.size(), entry.profileIds)
+                            .isOk());
+
+        auto it = encryptedBlobs.find(&entry);
+        ASSERT_NE(it, encryptedBlobs.end());
+        const vector<vector<uint8_t>>& encryptedChunks = it->second;
+
+        vector<uint8_t> content;
+        for (const auto& encryptedChunk : encryptedChunks) {
+            vector<uint8_t> chunk;
+            ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
+            content.insert(content.end(), chunk.begin(), chunk.end());
+        }
+        EXPECT_EQ(content, entry.valueCbor);
+
+        // TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
+        // the HAL returns...
+    }
+
+    vector<uint8_t> mac;
+    vector<uint8_t> deviceNameSpacesEncoded;
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
+    ASSERT_EQ(
+            "{\n"
+            "  'PersonalData' : {\n"
+            "    'Last name' : 'Turing',\n"
+            "    'Birth date' : '19120623',\n"
+            "    'First name' : 'Alan',\n"
+            "    'Home address' : 'Maida Vale, London, England',\n"
+            "  },\n"
+            "  'Image' : {\n"
+            "    'Portrait image' : <bstr size=262134 "
+            "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
+            "  },\n"
+            "}",
+            cborPretty);
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    optional<vector<uint8_t>> readerEphemeralPrivateKey =
+            support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
+    optional<vector<uint8_t>> eMacKey =
+            support::calcEMacKey(readerEphemeralPrivateKey.value(),  // Private Key
+                                 signingPubKey.value(),              // Public Key
+                                 cppbor::SemanticTag(24, sessionTranscript.encode())
+                                         .encode());  // SessionTranscriptBytes
+    optional<vector<uint8_t>> calculatedMac =
+            support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                             docType,                     // DocType
+                             deviceNameSpacesEncoded,     // DeviceNamespaces
+                             eMacKey.value());            // EMacKey
+    ASSERT_TRUE(calculatedMac);
+    EXPECT_EQ(mac, calculatedMac);
+
+    // Also perform an additional empty request. This is what mDL applications
+    // are envisioned to do - one call to get the data elements, another to get
+    // an empty DeviceSignedItems and corresponding MAC.
+    //
+    credential->setRequestedNamespaces({});  // OK to fail, not available in v1 HAL
+    ASSERT_TRUE(credential
+                        ->startRetrieval(
+                                secureProfiles.value(), authToken, {},         // itemsRequestBytes
+                                signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
+                                testEntriesEntryCounts)
+                        .isOk());
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
+    ASSERT_EQ("{}", cborPretty);
+    // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
+    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                                     docType,                     // DocType
+                                     deviceNameSpacesEncoded,     // DeviceNamespaces
+                                     eMacKey.value());            // EMacKey
+    ASSERT_TRUE(calculatedMac);
+    EXPECT_EQ(mac, calculatedMac);
+
+    // Some mDL apps might send a request but with a single empty
+    // namespace. Check that too.
+    RequestNamespace emptyRequestNS;
+    emptyRequestNS.namespaceName = "PersonalData";
+    credential->setRequestedNamespaces({emptyRequestNS});  // OK to fail, not available in v1 HAL
+    ASSERT_TRUE(credential
+                        ->startRetrieval(
+                                secureProfiles.value(), authToken, {},         // itemsRequestBytes
+                                signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
+                                testEntriesEntryCounts)
+                        .isOk());
+    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
+    cborPretty = cppbor::prettyPrint(deviceNameSpacesEncoded, 32, {});
+    ASSERT_EQ("{}", cborPretty);
+    // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
+    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
+                                     docType,                     // DocType
+                                     deviceNameSpacesEncoded,     // DeviceNamespaces
+                                     eMacKey.value());            // EMacKey
+    ASSERT_TRUE(calculatedMac);
+    EXPECT_EQ(mac, calculatedMac);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EndToEndTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, EndToEndTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
+    ::android::ProcessState::self()->startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/identity/aidl/vts/ProveOwnershipTests.cpp b/identity/aidl/vts/ProveOwnershipTests.cpp
new file mode 100644
index 0000000..c622193
--- /dev/null
+++ b/identity/aidl/vts/ProveOwnershipTests.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "ProveOwnershipTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class ProveOwnershipTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    void provisionData();
+
+    // Set by provisionData
+    vector<uint8_t> credentialData_;
+    vector<uint8_t> credentialPubKey_;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+void ProveOwnershipTests::provisionData() {
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    optional<vector<uint8_t>> optCredentialPubKey =
+            support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+    ASSERT_TRUE(optCredentialPubKey);
+    credentialPubKey_ = optCredentialPubKey.value();
+
+    size_t proofOfProvisioningSize = 106;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Some Data", 1).isOk());
+    vector<uint8_t> encryptedData;
+    ASSERT_TRUE(wc->addEntryValue({9}, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+}
+
+TEST_P(ProveOwnershipTests, proveOwnership) {
+    if (halApiVersion_ < 3) {
+        GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+    }
+
+    provisionData();
+
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &credential)
+                        .isOk());
+
+    vector<uint8_t> challenge = {17, 18};
+    vector<uint8_t> proofOfOwnershipSignature;
+    ASSERT_TRUE(credential->proveOwnership(challenge, &proofOfOwnershipSignature).isOk());
+    optional<vector<uint8_t>> proofOfOwnership =
+            support::coseSignGetPayload(proofOfOwnershipSignature);
+    ASSERT_TRUE(proofOfOwnership);
+    string cborPretty = cppbor::prettyPrint(proofOfOwnership.value(), 32, {});
+    EXPECT_EQ("['ProofOfOwnership', 'org.iso.18013-5.2019.mdl', {0x11, 0x12}, true, ]", cborPretty);
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfOwnershipSignature, {},  // Additional data
+                                                 credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ProveOwnershipTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, ProveOwnershipTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/ReaderAuthTests.cpp b/identity/aidl/vts/ReaderAuthTests.cpp
index b11f6c5..c4a58c3 100644
--- a/identity/aidl/vts/ReaderAuthTests.cpp
+++ b/identity/aidl/vts/ReaderAuthTests.cpp
@@ -32,7 +32,7 @@
 #include <map>
 #include <utility>
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
 
 namespace android::hardware::identity {
 
@@ -123,9 +123,9 @@
                                    const vector<uint8_t>& signingKey) {
     time_t validityNotBefore = 0;
     time_t validityNotAfter = 0xffffffff;
-    optional<vector<uint8_t>> cert =
-            support::ecPublicKeyGenerateCertificate(publicKey, signingKey, "24601", "Issuer",
-                                                    "Subject", validityNotBefore, validityNotAfter);
+    optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
+            publicKey, signingKey, "24601", "Issuer", "Subject", validityNotBefore,
+            validityNotAfter, {});
     return cert.value();
 }
 
@@ -262,8 +262,8 @@
     vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
     vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
     cppbor::Array sessionTranscript = cppbor::Array()
-                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
-                                              .add(cppbor::Semantic(24, eReaderPubBytes));
+                                              .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+                                              .add(cppbor::SemanticTag(24, eReaderPubBytes));
     vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
 
     vector<uint8_t> itemsRequestBytes;
@@ -293,10 +293,10 @@
             cppbor::Array()
                     .add("ReaderAuthentication")
                     .add(sessionTranscript.clone())
-                    .add(cppbor::Semantic(24, itemsRequestBytes))
+                    .add(cppbor::SemanticTag(24, itemsRequestBytes))
                     .encode();
     vector<uint8_t> encodedReaderAuthenticationBytes =
-            cppbor::Semantic(24, encodedReaderAuthentication).encode();
+            cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
 
     optional<vector<uint8_t>> readerSignature =
             support::coseSignEcDsa(readerPrivateKey,                  // private key for reader
@@ -517,8 +517,8 @@
     vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
     vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
     cppbor::Array sessionTranscript = cppbor::Array()
-                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
-                                              .add(cppbor::Semantic(24, eReaderPubBytes));
+                                              .add(cppbor::SemanticTag(24, deviceEngagementBytes))
+                                              .add(cppbor::SemanticTag(24, eReaderPubBytes));
     vector<uint8_t> sessionTranscriptBytes = sessionTranscript.encode();
 
     vector<uint8_t> itemsRequestBytes;
@@ -535,10 +535,10 @@
             cppbor::Array()
                     .add("ReaderAuthentication")
                     .add(sessionTranscript.clone())
-                    .add(cppbor::Semantic(24, itemsRequestBytes))
+                    .add(cppbor::SemanticTag(24, itemsRequestBytes))
                     .encode();
     vector<uint8_t> encodedReaderAuthenticationBytes =
-            cppbor::Semantic(24, encodedReaderAuthentication).encode();
+            cppbor::SemanticTag(24, encodedReaderAuthentication).encode();
 
     vector<vector<uint8_t>> readerCertChain = {cert_reader_SelfSigned_};
     optional<vector<uint8_t>> readerSignature =
@@ -594,6 +594,7 @@
               status.serviceSpecificErrorCode());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ReaderAuthTests);
 INSTANTIATE_TEST_SUITE_P(
         Identity, ReaderAuthTests,
         testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
diff --git a/identity/aidl/vts/TestCredentialTests.cpp b/identity/aidl/vts/TestCredentialTests.cpp
new file mode 100644
index 0000000..46c2229
--- /dev/null
+++ b/identity/aidl/vts/TestCredentialTests.cpp
@@ -0,0 +1,204 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TestCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class TestCredentialTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        string halInstanceName = GetParam();
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(halInstanceName.c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+TEST_P(TestCredentialTests, testCredential) {
+    string docType = "org.iso.18013-5.2019.mdl";
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_
+                        ->createCredential(docType,
+                                           true,  // testCredential
+                                           &wc)
+                        .isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    optional<vector<uint8_t>> optCredentialPubKey =
+            support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+    ASSERT_TRUE(optCredentialPubKey);
+    vector<uint8_t> credentialPubKey;
+    credentialPubKey = optCredentialPubKey.value();
+
+    size_t proofOfProvisioningSize = 112;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    vector<uint8_t> encryptedData;
+    vector<uint8_t> tstrLastName = cppbor::Tstr("Turing").encode();
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+    ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    vector<uint8_t> credentialData;
+    Status status = wc->finishAddingEntries(&credentialData, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'ns' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Turing',\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+    // Make sure it's signed by the CredentialKey in the returned cert chain.
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey));
+
+    // Now analyze credentialData..
+    auto [item, _, message] = cppbor::parse(credentialData);
+    ASSERT_NE(item, nullptr);
+    const cppbor::Array* arrayItem = item->asArray();
+    ASSERT_NE(arrayItem, nullptr);
+    ASSERT_EQ(arrayItem->size(), 3);
+    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
+    const cppbor::Bool* testCredentialItem =
+            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
+                                                    : nullptr);
+    EXPECT_EQ(docTypeItem->value(), docType);
+    EXPECT_EQ(testCredentialItem->value(), true);
+
+    vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
+    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
+    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
+    const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
+    optional<vector<uint8_t>> decryptedCredentialKeys =
+            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
+    ASSERT_TRUE(decryptedCredentialKeys);
+    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
+    ASSERT_NE(dckItem, nullptr) << dckMessage;
+    const cppbor::Array* dckArrayItem = dckItem->asArray();
+    ASSERT_NE(dckArrayItem, nullptr);
+    // In HAL API version 1 and 2 this array has two items, in version 3 and later it has three.
+    if (halApiVersion_ < 3) {
+        ASSERT_EQ(dckArrayItem->size(), 2);
+    } else {
+        ASSERT_EQ(dckArrayItem->size(), 3);
+    }
+    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
+    const vector<uint8_t> storageKey = storageKeyItem->value();
+    // const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
+    // const vector<uint8_t> credentialPrivKey = credentialPrivKeyItem->value();
+
+    // Check storageKey can be used to decrypt |encryptedData| to |tstrLastName|
+    vector<uint8_t> additionalData = cppbor::Map()
+                                             .add("Namespace", "ns")
+                                             .add("Name", "Last name")
+                                             .add("AccessControlProfileIds", cppbor::Array().add(1))
+                                             .encode();
+    optional<vector<uint8_t>> decryptedDataItemValue =
+            support::decryptAes128Gcm(storageKey, encryptedData, additionalData);
+    ASSERT_TRUE(decryptedDataItemValue);
+    EXPECT_EQ(decryptedDataItemValue.value(), tstrLastName);
+
+    // Check that SHA-256(ProofOfProvisioning) matches (only in HAL API version 3)
+    if (halApiVersion_ >= 3) {
+        const cppbor::Bstr* popSha256Item = (*dckArrayItem)[2]->asBstr();
+        const vector<uint8_t> popSha256 = popSha256Item->value();
+        ASSERT_EQ(popSha256, support::sha256(proofOfProvisioning.value()));
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, TestCredentialTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UpdateCredentialTests.cpp b/identity/aidl/vts/UpdateCredentialTests.cpp
new file mode 100644
index 0000000..e05bb3b
--- /dev/null
+++ b/identity/aidl/vts/UpdateCredentialTests.cpp
@@ -0,0 +1,232 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "UpdateCredentialTests"
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/keymaster/HardwareAuthToken.h>
+#include <aidl/android/hardware/keymaster/VerificationToken.h>
+#include <android-base/logging.h>
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+#include <future>
+#include <map>
+#include <utility>
+
+#include "Util.h"
+
+namespace android::hardware::identity {
+
+using std::endl;
+using std::make_pair;
+using std::map;
+using std::optional;
+using std::pair;
+using std::string;
+using std::tie;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::binder::Status;
+
+using ::android::hardware::keymaster::HardwareAuthToken;
+using ::android::hardware::keymaster::VerificationToken;
+
+class UpdateCredentialTests : public testing::TestWithParam<string> {
+  public:
+    virtual void SetUp() override {
+        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
+                String16(GetParam().c_str()));
+        ASSERT_NE(credentialStore_, nullptr);
+        halApiVersion_ = credentialStore_->getInterfaceVersion();
+    }
+
+    void provisionData();
+
+    // Set by provisionData
+    vector<uint8_t> credentialData_;
+    vector<uint8_t> credentialPubKey_;
+
+    sp<IIdentityCredentialStore> credentialStore_;
+    int halApiVersion_;
+};
+
+void UpdateCredentialTests::provisionData() {
+    string docType = "org.iso.18013-5.2019.mdl";
+    bool testCredential = true;
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credentialStore_->createCredential(docType, testCredential, &wc).isOk());
+
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    ASSERT_TRUE(wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                              &certChain)
+                        .isOk());
+
+    optional<vector<uint8_t>> optCredentialPubKey =
+            support::certificateChainGetTopMostKey(certChain[0].encodedCertificate);
+    ASSERT_TRUE(optCredentialPubKey);
+    credentialPubKey_ = optCredentialPubKey.value();
+
+    size_t proofOfProvisioningSize = 112;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(1, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    vector<uint8_t> encryptedData;
+    vector<uint8_t> tstrLastName = cppbor::Tstr("Prince").encode();
+    ASSERT_TRUE(wc->beginAddEntry({1}, "ns", "Last name", tstrLastName.size()).isOk());
+    ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 1,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'ns' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'Prince',\n"
+            "        'accessControlProfiles' : [1, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+    // Make sure it's signed by the CredentialKey in the returned cert chain.
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey_));
+}
+
+TEST_P(UpdateCredentialTests, updateCredential) {
+    if (halApiVersion_ < 3) {
+        GTEST_SKIP() << "Need HAL API version 3, have " << halApiVersion_;
+    }
+
+    provisionData();
+
+    sp<IIdentityCredential> credential;
+    ASSERT_TRUE(credentialStore_
+                        ->getCredential(
+                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
+                                credentialData_, &credential)
+                        .isOk());
+
+    sp<IWritableIdentityCredential> wc;
+    ASSERT_TRUE(credential->updateCredential(&wc).isOk());
+
+    // Getting an attestation cert should fail (because it's an update).
+    vector<uint8_t> attestationApplicationId = {};
+    vector<uint8_t> attestationChallenge = {1};
+    vector<Certificate> certChain;
+    Status result = wc->getAttestationCertificate(attestationApplicationId, attestationChallenge,
+                                                  &certChain);
+    ASSERT_FALSE(result.isOk());
+    EXPECT_EQ(binder::Status::EX_SERVICE_SPECIFIC, result.exceptionCode());
+    EXPECT_EQ(IIdentityCredentialStore::STATUS_FAILED, result.serviceSpecificErrorCode());
+
+    // Now provision some new data...
+    //
+    size_t proofOfProvisioningSize = 117;
+    // Not in v1 HAL, may fail
+    wc->setExpectedProofOfProvisioningSize(proofOfProvisioningSize);
+
+    ASSERT_TRUE(wc->startPersonalization(1 /* numAccessControlProfiles */,
+                                         {1} /* numDataElementsPerNamespace */)
+                        .isOk());
+
+    // Access control profile 0: open access - don't care about the returned SACP
+    SecureAccessControlProfile sacp;
+    ASSERT_TRUE(wc->addAccessControlProfile(2, {}, false, 0, 0, &sacp).isOk());
+
+    // Single entry - don't care about the returned encrypted data
+    vector<uint8_t> encryptedData;
+    vector<uint8_t> tstrLastName = cppbor::Tstr("T.A.F.K.A.P").encode();
+    ASSERT_TRUE(wc->beginAddEntry({2}, "ns", "Last name", tstrLastName.size()).isOk());
+    ASSERT_TRUE(wc->addEntryValue(tstrLastName, &encryptedData).isOk());
+
+    vector<uint8_t> proofOfProvisioningSignature;
+    Status status = wc->finishAddingEntries(&credentialData_, &proofOfProvisioningSignature);
+    EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
+    optional<vector<uint8_t>> proofOfProvisioning =
+            support::coseSignGetPayload(proofOfProvisioningSignature);
+    ASSERT_TRUE(proofOfProvisioning);
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {});
+    EXPECT_EQ(
+            "[\n"
+            "  'ProofOfProvisioning',\n"
+            "  'org.iso.18013-5.2019.mdl',\n"
+            "  [\n"
+            "    {\n"
+            "      'id' : 2,\n"
+            "    },\n"
+            "  ],\n"
+            "  {\n"
+            "    'ns' : [\n"
+            "      {\n"
+            "        'name' : 'Last name',\n"
+            "        'value' : 'T.A.F.K.A.P',\n"
+            "        'accessControlProfiles' : [2, ],\n"
+            "      },\n"
+            "    ],\n"
+            "  },\n"
+            "  true,\n"
+            "]",
+            cborPretty);
+    // Make sure it's signed by the same CredentialKey we originally provisioned with.
+    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
+                                                 {},  // Additional data
+                                                 credentialPubKey_));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UpdateCredentialTests);
+INSTANTIATE_TEST_SUITE_P(
+        Identity, UpdateCredentialTests,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
+        android::PrintInstanceNameToString);
+
+}  // namespace android::hardware::identity
diff --git a/identity/aidl/vts/UserAuthTests.cpp b/identity/aidl/vts/UserAuthTests.cpp
index 5b4c8f1..edd1725 100644
--- a/identity/aidl/vts/UserAuthTests.cpp
+++ b/identity/aidl/vts/UserAuthTests.cpp
@@ -32,7 +32,7 @@
 #include <map>
 #include <utility>
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
 
 namespace android::hardware::identity {
 
@@ -145,7 +145,7 @@
     EXPECT_TRUE(status.isOk()) << status.exceptionCode() << ": " << status.exceptionMessage();
 }
 
-// From ReaderAuthTest.cpp - TODO: consolidate with VtsIdentityTestUtils.h
+// From ReaderAuthTest.cpp - TODO: consolidate with Util.h
 pair<vector<uint8_t>, vector<uint8_t>> generateReaderKey();
 vector<uint8_t> generateReaderCert(const vector<uint8_t>& publicKey,
                                    const vector<uint8_t>& signingKey);
@@ -160,8 +160,8 @@
     // Let SessionTranscript be a map here (it's an array in EndToEndTest) just
     // to check that the implementation can deal with either.
     cppbor::Map sessionTranscript;
-    sessionTranscript.add(42, cppbor::Semantic(24, deviceEngagementBytes));
-    sessionTranscript.add(43, cppbor::Semantic(24, eReaderPubBytes));
+    sessionTranscript.add(42, cppbor::SemanticTag(24, deviceEngagementBytes));
+    sessionTranscript.add(43, cppbor::SemanticTag(24, eReaderPubBytes));
     return sessionTranscript;
 }
 
@@ -209,7 +209,7 @@
         vector<uint8_t> dataToSign = cppbor::Array()
                                              .add("ReaderAuthentication")
                                              .add(sessionTranscript_.clone())
-                                             .add(cppbor::Semantic(24, itemsRequestBytes))
+                                             .add(cppbor::SemanticTag(24, itemsRequestBytes))
                                              .encode();
     }
 
@@ -465,6 +465,7 @@
                  true /* useSessionTranscript */);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UserAuthTests);
 INSTANTIATE_TEST_SUITE_P(
         Identity, UserAuthTests,
         testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
diff --git a/identity/aidl/vts/Util.cpp b/identity/aidl/vts/Util.cpp
new file mode 100644
index 0000000..1148cb0
--- /dev/null
+++ b/identity/aidl/vts/Util.cpp
@@ -0,0 +1,476 @@
+/*
+ * Copyright 2019, 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.
+ */
+
+#define LOG_TAG "Util"
+
+#include "Util.h"
+
+#include <android-base/logging.h>
+
+#include <aidl/Gtest.h>
+#include <android-base/stringprintf.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <keymasterV4_1/attestation_record.h>
+#include <charconv>
+
+#include <map>
+
+namespace android::hardware::identity::test_utils {
+
+using std::endl;
+using std::map;
+using std::optional;
+using std::string;
+using std::vector;
+
+using ::android::sp;
+using ::android::String16;
+using ::android::base::StringPrintf;
+using ::android::binder::Status;
+using ::keymaster::X509_Ptr;
+
+bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
+                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential) {
+    if (credentialStore == nullptr) {
+        return false;
+    }
+
+    string docType = "org.iso.18013-5.2019.mdl";
+    Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
+
+    if (result.isOk() && writableCredential != nullptr) {
+        return true;
+    } else {
+        return false;
+    }
+}
+
+optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal) {
+    vector<uint8_t> privKey;
+    return generateReaderCertificate(serialDecimal, &privKey);
+}
+
+optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
+                                                    vector<uint8_t>* outReaderPrivateKey) {
+    optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
+    if (!readerKeyPKCS8) {
+        return {};
+    }
+
+    optional<vector<uint8_t>> readerPublicKey =
+            support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
+    optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
+    if (!readerPublicKey || !readerKey) {
+        return {};
+    }
+
+    if (outReaderPrivateKey == nullptr) {
+        return {};
+    }
+
+    *outReaderPrivateKey = readerKey.value();
+
+    string issuer = "Android Open Source Project";
+    string subject = "Android IdentityCredential VTS Test";
+    time_t validityNotBefore = time(nullptr);
+    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
+
+    return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
+                                                   serialDecimal, issuer, subject,
+                                                   validityNotBefore, validityNotAfter, {});
+}
+
+optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
+        sp<IWritableIdentityCredential>& writableCredential,
+        const vector<TestProfile>& testProfiles) {
+    Status result;
+
+    vector<SecureAccessControlProfile> secureProfiles;
+
+    for (const auto& testProfile : testProfiles) {
+        SecureAccessControlProfile profile;
+        Certificate cert;
+        cert.encodedCertificate = testProfile.readerCertificate;
+        int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
+        result = writableCredential->addAccessControlProfile(
+                testProfile.id, cert, testProfile.userAuthenticationRequired,
+                testProfile.timeoutMillis, secureUserId, &profile);
+
+        // Don't use assert so all errors can be outputed.  Then return
+        // instead of exit even on errors so caller can decide.
+        EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
+                                   << "test profile id = " << testProfile.id << endl;
+        EXPECT_EQ(testProfile.id, profile.id);
+        EXPECT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
+        EXPECT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
+        EXPECT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
+        EXPECT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
+
+        if (!result.isOk() || testProfile.id != profile.id ||
+            testProfile.readerCertificate != profile.readerCertificate.encodedCertificate ||
+            testProfile.userAuthenticationRequired != profile.userAuthenticationRequired ||
+            testProfile.timeoutMillis != profile.timeoutMillis ||
+            support::kAesGcmTagSize + support::kAesGcmIvSize != profile.mac.size()) {
+            return {};
+        }
+
+        secureProfiles.push_back(profile);
+    }
+
+    return secureProfiles;
+}
+
+// Most test expects this function to pass. So we will print out additional
+// value if failed so more debug data can be provided.
+bool addEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
+              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
+              bool expectSuccess) {
+    Status result;
+    vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize);
+
+    result = writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
+                                               entry.valueCbor.size());
+
+    if (expectSuccess) {
+        EXPECT_TRUE(result.isOk())
+                << result.exceptionCode() << "; " << result.exceptionMessage() << endl
+                << "entry name = " << entry.name << ", name space=" << entry.nameSpace << endl;
+    }
+
+    if (!result.isOk()) {
+        return false;
+    }
+
+    vector<vector<uint8_t>> encryptedChunks;
+    for (const auto& chunk : chunks) {
+        vector<uint8_t> encryptedContent;
+        result = writableCredential->addEntryValue(chunk, &encryptedContent);
+        if (expectSuccess) {
+            EXPECT_TRUE(result.isOk())
+                    << result.exceptionCode() << "; " << result.exceptionMessage() << endl
+                    << "entry name = " << entry.name << ", name space = " << entry.nameSpace
+                    << endl;
+
+            EXPECT_GT(encryptedContent.size(), 0u) << "entry name = " << entry.name
+                                                   << ", name space = " << entry.nameSpace << endl;
+        }
+
+        if (!result.isOk() || encryptedContent.size() <= 0u) {
+            return false;
+        }
+
+        encryptedChunks.push_back(encryptedContent);
+    }
+
+    encryptedBlobs[&entry] = encryptedChunks;
+    return true;
+}
+
+void setImageData(vector<uint8_t>& image) {
+    image.resize(256 * 1024 - 10);
+    for (size_t n = 0; n < image.size(); n++) {
+        image[n] = (uint8_t)n;
+    }
+}
+
+string x509NameToRfc2253String(X509_NAME* name) {
+    char* buf;
+    size_t bufSize;
+    BIO* bio;
+
+    bio = BIO_new(BIO_s_mem());
+    X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
+    bufSize = BIO_get_mem_data(bio, &buf);
+    string ret = string(buf, bufSize);
+    BIO_free(bio);
+
+    return ret;
+}
+
+int parseDigits(const char** s, int numDigits) {
+    int result;
+    auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
+    if (ec != std::errc()) {
+        LOG(ERROR) << "Error parsing " << numDigits << " digits "
+                   << " from " << s;
+        return 0;
+    }
+    *s += numDigits;
+    return result;
+}
+
+bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
+    struct tm tm;
+
+    memset(&tm, '\0', sizeof(tm));
+    const char* timeStr = (const char*)asn1Time->data;
+    const char* s = timeStr;
+    if (asn1Time->type == V_ASN1_UTCTIME) {
+        tm.tm_year = parseDigits(&s, 2);
+        if (tm.tm_year < 70) {
+            tm.tm_year += 100;
+        }
+    } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
+        tm.tm_year = parseDigits(&s, 4) - 1900;
+        tm.tm_year -= 1900;
+    } else {
+        LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
+        return false;
+    }
+    tm.tm_mon = parseDigits(&s, 2) - 1;
+    tm.tm_mday = parseDigits(&s, 2);
+    tm.tm_hour = parseDigits(&s, 2);
+    tm.tm_min = parseDigits(&s, 2);
+    tm.tm_sec = parseDigits(&s, 2);
+    // This may need to be updated if someone create certificates using +/- instead of Z.
+    //
+    if (*s != 'Z') {
+        LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
+        return false;
+    }
+
+    time_t t = timegm(&tm);
+    if (t == -1) {
+        LOG(ERROR) << "Error converting broken-down time to time_t";
+        return false;
+    }
+    *outTime = t;
+    return true;
+}
+
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
+                                    const vector<uint8_t>& expectedChallenge,
+                                    const vector<uint8_t>& expectedAppId, bool isTestCredential) {
+    ASSERT_GE(credentialKeyCertChain.size(), 2);
+
+    vector<uint8_t> certBytes = credentialKeyCertChain[0].encodedCertificate;
+    const uint8_t* certData = certBytes.data();
+    X509_Ptr cert = X509_Ptr(d2i_X509(nullptr, &certData, certBytes.size()));
+
+    vector<uint8_t> batchCertBytes = credentialKeyCertChain[1].encodedCertificate;
+    const uint8_t* batchCertData = batchCertBytes.data();
+    X509_Ptr batchCert = X509_Ptr(d2i_X509(nullptr, &batchCertData, batchCertBytes.size()));
+
+    // First get some values from the batch certificate which is checked
+    // against the top-level certificate (subject, notAfter)
+    //
+
+    X509_NAME* batchSubject = X509_get_subject_name(batchCert.get());
+    ASSERT_NE(nullptr, batchSubject);
+    time_t batchNotAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(batchCert.get()), &batchNotAfter));
+
+    // Check all the requirements from IWritableIdentityCredential::getAttestationCertificate()...
+    //
+
+    //  - version: INTEGER 2 (means v3 certificate).
+    EXPECT_EQ(2, X509_get_version(cert.get()));
+
+    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
+    EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
+
+    //  - signature: must be set to ECDSA.
+    EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
+
+    //  - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
+    //    same on all certs)
+    X509_NAME* subject = X509_get_subject_name(cert.get());
+    ASSERT_NE(nullptr, subject);
+    EXPECT_EQ("CN=Android Identity Credential Key", x509NameToRfc2253String(subject));
+
+    //  - issuer: Same as the subject field of the batch attestation key.
+    X509_NAME* issuer = X509_get_issuer_name(cert.get());
+    ASSERT_NE(nullptr, issuer);
+    EXPECT_EQ(x509NameToRfc2253String(batchSubject), x509NameToRfc2253String(issuer));
+
+    //  - validity: Should be from current time and expire at the same time as the
+    //    attestation batch certificate used.
+    //
+    //  Allow for 10 seconds drift to account for the time drift between Secure HW
+    //  and this environment plus the difference between when the certificate was
+    //  created and until now
+    //
+    time_t notBefore;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+    uint64_t now = time(nullptr);
+    int64_t diffSecs = now - notBefore;
+    int64_t allowDriftSecs = 10;
+    EXPECT_LE(-allowDriftSecs, diffSecs);
+    EXPECT_GE(allowDriftSecs, diffSecs);
+
+    time_t notAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+    EXPECT_EQ(notAfter, batchNotAfter);
+
+    auto [err, attRec] = keymaster::V4_1::parse_attestation_record(certBytes);
+    ASSERT_EQ(keymaster::V4_1::ErrorCode::OK, err);
+
+    //  - subjectPublicKeyInfo: must contain attested public key.
+
+    //  - The attestationVersion field in the attestation extension must be at least 3.
+    EXPECT_GE(attRec.attestation_version, 3);
+
+    //  - The attestationSecurityLevel field must be set to either Software (0),
+    //    TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
+    //    implemented.
+    EXPECT_GE(attRec.attestation_security_level,
+              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //  - The keymasterVersion field in the attestation extension must be set to the.
+    //    same value as used for Android Keystore keys.
+    //
+    // Nothing to check here...
+
+    //  - The keymasterSecurityLevel field in the attestation extension must be set to
+    //    either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
+    //    the Trusted Application backing the HAL implementation is implemented.
+    EXPECT_GE(attRec.keymaster_security_level, keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //  - The attestationChallenge field must be set to the passed-in challenge.
+    EXPECT_EQ(expectedChallenge.size(), attRec.attestation_challenge.size());
+    EXPECT_TRUE(memcmp(expectedChallenge.data(), attRec.attestation_challenge.data(),
+                       attRec.attestation_challenge.size()) == 0);
+
+    //  - The uniqueId field must be empty.
+    EXPECT_EQ(attRec.unique_id.size(), 0);
+
+    //  - The softwareEnforced field in the attestation extension must include
+    //    Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
+    //    attestationApplicationId.
+    EXPECT_TRUE(attRec.software_enforced.Contains(keymaster::V4_0::TAG_ATTESTATION_APPLICATION_ID,
+                                                  expectedAppId));
+
+    //  - The teeEnforced field in the attestation extension must include
+    //
+    //    - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
+    //      Credential key (which can only sign/MAC very specific messages) and not an Android
+    //      Keystore key (which can be used to sign/MAC anything). This must not be set
+    //      for test credentials.
+    bool hasIcKeyTag =
+            attRec.hardware_enforced.Contains(static_cast<android::hardware::keymaster::V4_0::Tag>(
+                    keymaster::V4_1::Tag::IDENTITY_CREDENTIAL_KEY));
+    if (isTestCredential) {
+        EXPECT_FALSE(hasIcKeyTag);
+    } else {
+        EXPECT_TRUE(hasIcKeyTag);
+    }
+
+    //    - Tag::PURPOSE must be set to SIGN
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_PURPOSE,
+                                                  keymaster::V4_0::KeyPurpose::SIGN));
+
+    //    - Tag::KEY_SIZE must be set to the appropriate key size, in bits (e.g. 256)
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_KEY_SIZE, 256));
+
+    //    - Tag::ALGORITHM must be set to EC
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_ALGORITHM,
+                                                  keymaster::V4_0::Algorithm::EC));
+
+    //    - Tag::NO_AUTH_REQUIRED must be set
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_NO_AUTH_REQUIRED));
+
+    //    - Tag::DIGEST must be include SHA_2_256
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_DIGEST,
+                                                  keymaster::V4_0::Digest::SHA_2_256));
+
+    //    - Tag::EC_CURVE must be set to P_256
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_EC_CURVE,
+                                                  keymaster::V4_0::EcCurve::P_256));
+
+    //    - Tag::ROOT_OF_TRUST must be set
+    //
+    EXPECT_GE(attRec.root_of_trust.security_level,
+              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
+
+    //    - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_VERSION));
+    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_PATCHLEVEL));
+
+    // TODO: we could retrieve osVersion and osPatchLevel from Android itself and compare it
+    // with what was reported in the certificate.
+}
+
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain) {
+    const uint8_t* data = authKeyCertChain.data();
+    auto cert = X509_Ptr(d2i_X509(nullptr, &data, authKeyCertChain.size()));
+
+    //  - version: INTEGER 2 (means v3 certificate).
+    EXPECT_EQ(X509_get_version(cert.get()), 2);
+
+    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
+    EXPECT_EQ(ASN1_INTEGER_get(X509_get_serialNumber(cert.get())), 1);
+
+    //  - signature: must be set to ECDSA.
+    EXPECT_EQ(X509_get_signature_nid(cert.get()), NID_ecdsa_with_SHA256);
+
+    //  - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
+    //    value: same on all certs)
+    X509_NAME* subject = X509_get_subject_name(cert.get());
+    ASSERT_NE(subject, nullptr);
+    EXPECT_EQ(x509NameToRfc2253String(subject),
+              "CN=Android Identity Credential Authentication Key");
+
+    //  - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
+    //    same on all certs)
+    X509_NAME* issuer = X509_get_issuer_name(cert.get());
+    ASSERT_NE(issuer, nullptr);
+    EXPECT_EQ(x509NameToRfc2253String(issuer), "CN=Android Identity Credential Key");
+
+    //  - subjectPublicKeyInfo: must contain attested public key.
+
+    //  - validity: should be from current time and one year in the future (365 days).
+    time_t notBefore, notAfter;
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
+    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
+
+    //  Allow for 10 seconds drift to account for the time drift between Secure HW
+    //  and this environment plus the difference between when the certificate was
+    //  created and until now
+    //
+    uint64_t now = time(nullptr);
+    int64_t diffSecs = now - notBefore;
+    int64_t allowDriftSecs = 10;
+    EXPECT_LE(-allowDriftSecs, diffSecs);
+    EXPECT_GE(allowDriftSecs, diffSecs);
+    constexpr uint64_t kSecsInOneYear = 365 * 24 * 60 * 60;
+    EXPECT_EQ(notBefore + kSecsInOneYear, notAfter);
+}
+
+vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
+    vector<RequestNamespace> ret;
+    RequestNamespace curNs;
+    for (const TestEntryData& testEntry : entries) {
+        if (testEntry.nameSpace != curNs.namespaceName) {
+            if (curNs.namespaceName.size() > 0) {
+                ret.push_back(curNs);
+            }
+            curNs.namespaceName = testEntry.nameSpace;
+            curNs.items.clear();
+        }
+
+        RequestDataItem item;
+        item.name = testEntry.name;
+        item.size = testEntry.valueCbor.size();
+        item.accessControlProfileIds = testEntry.profileIds;
+        curNs.items.push_back(item);
+    }
+    if (curNs.namespaceName.size() > 0) {
+        ret.push_back(curNs);
+    }
+    return ret;
+}
+
+}  // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/Util.h b/identity/aidl/vts/Util.h
new file mode 100644
index 0000000..80e52a2
--- /dev/null
+++ b/identity/aidl/vts/Util.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2019, 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 VTS_IDENTITY_TEST_UTILS_H
+#define VTS_IDENTITY_TEST_UTILS_H
+
+#include <android/hardware/identity/IIdentityCredentialStore.h>
+#include <android/hardware/identity/support/IdentityCredentialSupport.h>
+#include <cppbor.h>
+#include <cppbor_parse.h>
+#include <gtest/gtest.h>
+
+namespace android::hardware::identity::test_utils {
+
+using ::std::map;
+using ::std::optional;
+using ::std::string;
+using ::std::vector;
+
+using ::android::sp;
+using ::android::binder::Status;
+
+struct AttestationData {
+    AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
+                    vector<uint8_t> attestationAppId)
+        : attestationApplicationId(attestationAppId) {
+        // ASSERT_NE(writableCredential, nullptr);
+
+        if (!challenge.empty()) {
+            attestationChallenge.assign(challenge.begin(), challenge.end());
+        }
+
+        result = writableCredential->getAttestationCertificate(
+                attestationApplicationId, attestationChallenge, &attestationCertificate);
+    }
+
+    AttestationData() {}
+
+    vector<uint8_t> attestationChallenge;
+    vector<uint8_t> attestationApplicationId;
+    vector<Certificate> attestationCertificate;
+    Status result;
+};
+
+struct TestEntryData {
+    TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
+        : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
+
+    TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
+    }
+    TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
+                  vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bstr(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        valueCbor = cppbor::Bool(value).encode();
+    }
+    TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
+        : TestEntryData(nameSpace, name, profileIds) {
+        if (value >= 0) {
+            valueCbor = cppbor::Uint(value).encode();
+        } else {
+            valueCbor = cppbor::Nint(-value).encode();
+        }
+    }
+
+    string nameSpace;
+    string name;
+    vector<uint8_t> valueCbor;
+    vector<int32_t> profileIds;
+};
+
+struct TestProfile {
+    uint16_t id;
+    vector<uint8_t> readerCertificate;
+    bool userAuthenticationRequired;
+    uint64_t timeoutMillis;
+};
+
+bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
+                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
+
+optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
+
+optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
+                                                    vector<uint8_t>* outReaderPrivateKey);
+
+optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
+        sp<IWritableIdentityCredential>& writableCredential,
+        const vector<TestProfile>& testProfiles);
+
+bool addEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
+              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
+              bool expectSuccess);
+
+void setImageData(vector<uint8_t>& image);
+
+void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
+                                    const vector<uint8_t>& expectedChallenge,
+                                    const vector<uint8_t>& expectedAppId, bool isTestCredential);
+
+vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
+
+// Verifies that the X.509 certificate for a just created authentication key
+// is valid.
+//
+void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain);
+
+}  // namespace android::hardware::identity::test_utils
+
+#endif  // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/aidl/vts/VtsAttestationTests.cpp b/identity/aidl/vts/VtsAttestationTests.cpp
index e5871fb..e12fe05 100644
--- a/identity/aidl/vts/VtsAttestationTests.cpp
+++ b/identity/aidl/vts/VtsAttestationTests.cpp
@@ -29,7 +29,7 @@
 #include <future>
 #include <map>
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
 
 namespace android::hardware::identity {
 
@@ -105,6 +105,7 @@
                                    attestationApplicationId, false);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VtsAttestationTests);
 INSTANTIATE_TEST_SUITE_P(
         Identity, VtsAttestationTests,
         testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
diff --git a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp b/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
deleted file mode 100644
index 7657943..0000000
--- a/identity/aidl/vts/VtsHalIdentityEndToEndTest.cpp
+++ /dev/null
@@ -1,515 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-#define LOG_TAG "VtsHalIdentityEndToEndTest"
-
-#include <aidl/Gtest.h>
-#include <aidl/Vintf.h>
-#include <android-base/logging.h>
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-#include <binder/IServiceManager.h>
-#include <binder/ProcessState.h>
-#include <cppbor.h>
-#include <cppbor_parse.h>
-#include <gtest/gtest.h>
-#include <future>
-#include <map>
-#include <tuple>
-
-#include "VtsIdentityTestUtils.h"
-
-namespace android::hardware::identity {
-
-using std::endl;
-using std::make_tuple;
-using std::map;
-using std::optional;
-using std::string;
-using std::tuple;
-using std::vector;
-
-using ::android::sp;
-using ::android::String16;
-using ::android::binder::Status;
-
-using ::android::hardware::keymaster::HardwareAuthToken;
-using ::android::hardware::keymaster::VerificationToken;
-
-using test_utils::validateAttestationCertificate;
-
-class IdentityAidl : public testing::TestWithParam<std::string> {
-  public:
-    virtual void SetUp() override {
-        credentialStore_ = android::waitForDeclaredService<IIdentityCredentialStore>(
-                String16(GetParam().c_str()));
-        ASSERT_NE(credentialStore_, nullptr);
-    }
-
-    sp<IIdentityCredentialStore> credentialStore_;
-};
-
-TEST_P(IdentityAidl, hardwareInformation) {
-    HardwareInformation info;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&info).isOk());
-    ASSERT_GT(info.credentialStoreName.size(), 0);
-    ASSERT_GT(info.credentialStoreAuthorName.size(), 0);
-    ASSERT_GE(info.dataChunkSize, 256);
-}
-
-tuple<bool, string, vector<uint8_t>, vector<uint8_t>> extractFromTestCredentialData(
-        const vector<uint8_t>& credentialData) {
-    string docType;
-    vector<uint8_t> storageKey;
-    vector<uint8_t> credentialPrivKey;
-
-    auto [item, _, message] = cppbor::parse(credentialData);
-    if (item == nullptr) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-
-    const cppbor::Array* arrayItem = item->asArray();
-    if (arrayItem == nullptr || arrayItem->size() != 3) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-
-    const cppbor::Tstr* docTypeItem = (*arrayItem)[0]->asTstr();
-    const cppbor::Bool* testCredentialItem =
-            ((*arrayItem)[1]->asSimple() != nullptr ? ((*arrayItem)[1]->asSimple()->asBool())
-                                                    : nullptr);
-    const cppbor::Bstr* encryptedCredentialKeysItem = (*arrayItem)[2]->asBstr();
-    if (docTypeItem == nullptr || testCredentialItem == nullptr ||
-        encryptedCredentialKeysItem == nullptr) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-
-    docType = docTypeItem->value();
-
-    vector<uint8_t> hardwareBoundKey = support::getTestHardwareBoundKey();
-    const vector<uint8_t>& encryptedCredentialKeys = encryptedCredentialKeysItem->value();
-    const vector<uint8_t> docTypeVec(docType.begin(), docType.end());
-    optional<vector<uint8_t>> decryptedCredentialKeys =
-            support::decryptAes128Gcm(hardwareBoundKey, encryptedCredentialKeys, docTypeVec);
-    if (!decryptedCredentialKeys) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-
-    auto [dckItem, dckPos, dckMessage] = cppbor::parse(decryptedCredentialKeys.value());
-    if (dckItem == nullptr) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-    const cppbor::Array* dckArrayItem = dckItem->asArray();
-    if (dckArrayItem == nullptr || dckArrayItem->size() != 2) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-    const cppbor::Bstr* storageKeyItem = (*dckArrayItem)[0]->asBstr();
-    const cppbor::Bstr* credentialPrivKeyItem = (*dckArrayItem)[1]->asBstr();
-    if (storageKeyItem == nullptr || credentialPrivKeyItem == nullptr) {
-        return make_tuple(false, docType, storageKey, credentialPrivKey);
-    }
-    storageKey = storageKeyItem->value();
-    credentialPrivKey = credentialPrivKeyItem->value();
-    return make_tuple(true, docType, storageKey, credentialPrivKey);
-}
-
-TEST_P(IdentityAidl, createAndRetrieveCredential) {
-    // First, generate a key-pair for the reader since its public key will be
-    // part of the request data.
-    vector<uint8_t> readerKey;
-    optional<vector<uint8_t>> readerCertificate =
-            test_utils::generateReaderCertificate("1234", &readerKey);
-    ASSERT_TRUE(readerCertificate);
-
-    // Make the portrait image really big (just shy of 256 KiB) to ensure that
-    // the chunking code gets exercised.
-    vector<uint8_t> portraitImage;
-    test_utils::setImageData(portraitImage);
-
-    // Access control profiles:
-    const vector<test_utils::TestProfile> testProfiles = {// Profile 0 (reader authentication)
-                                                          {0, readerCertificate.value(), false, 0},
-                                                          // Profile 1 (no authentication)
-                                                          {1, {}, false, 0}};
-
-    // It doesn't matter since no user auth is needed in this particular test,
-    // but for good measure, clear out the tokens we pass to the HAL.
-    HardwareAuthToken authToken;
-    VerificationToken verificationToken;
-    authToken.challenge = 0;
-    authToken.userId = 0;
-    authToken.authenticatorId = 0;
-    authToken.authenticatorType = ::android::hardware::keymaster::HardwareAuthenticatorType::NONE;
-    authToken.timestamp.milliSeconds = 0;
-    authToken.mac.clear();
-    verificationToken.challenge = 0;
-    verificationToken.timestamp.milliSeconds = 0;
-    verificationToken.securityLevel = ::android::hardware::keymaster::SecurityLevel::SOFTWARE;
-    verificationToken.mac.clear();
-
-    // Here's the actual test data:
-    const vector<test_utils::TestEntryData> testEntries = {
-            {"PersonalData", "Last name", string("Turing"), vector<int32_t>{0, 1}},
-            {"PersonalData", "Birth date", string("19120623"), vector<int32_t>{0, 1}},
-            {"PersonalData", "First name", string("Alan"), vector<int32_t>{0, 1}},
-            {"PersonalData", "Home address", string("Maida Vale, London, England"),
-             vector<int32_t>{0}},
-            {"Image", "Portrait image", portraitImage, vector<int32_t>{0, 1}},
-    };
-    const vector<int32_t> testEntriesEntryCounts = {static_cast<int32_t>(testEntries.size() - 1),
-                                                    1u};
-    HardwareInformation hwInfo;
-    ASSERT_TRUE(credentialStore_->getHardwareInformation(&hwInfo).isOk());
-
-    string cborPretty;
-    sp<IWritableIdentityCredential> writableCredential;
-    ASSERT_TRUE(test_utils::setupWritableCredential(writableCredential, credentialStore_,
-                                                    true /* testCredential */));
-
-    string challenge = "attestationChallenge";
-    test_utils::AttestationData attData(writableCredential, challenge,
-                                        {1} /* atteestationApplicationId */);
-    ASSERT_TRUE(attData.result.isOk())
-            << attData.result.exceptionCode() << "; " << attData.result.exceptionMessage() << endl;
-
-    validateAttestationCertificate(attData.attestationCertificate, attData.attestationChallenge,
-                                   attData.attestationApplicationId, true);
-
-    // This is kinda of a hack but we need to give the size of
-    // ProofOfProvisioning that we'll expect to receive.
-    const int32_t expectedProofOfProvisioningSize = 262861 - 326 + readerCertificate.value().size();
-    // OK to fail, not available in v1 HAL
-    writableCredential->setExpectedProofOfProvisioningSize(expectedProofOfProvisioningSize);
-    ASSERT_TRUE(
-            writableCredential->startPersonalization(testProfiles.size(), testEntriesEntryCounts)
-                    .isOk());
-
-    optional<vector<SecureAccessControlProfile>> secureProfiles =
-            test_utils::addAccessControlProfiles(writableCredential, testProfiles);
-    ASSERT_TRUE(secureProfiles);
-
-    // Uses TestEntryData* pointer as key and values are the encrypted blobs. This
-    // is a little hacky but it works well enough.
-    map<const test_utils::TestEntryData*, vector<vector<uint8_t>>> encryptedBlobs;
-
-    for (const auto& entry : testEntries) {
-        ASSERT_TRUE(test_utils::addEntry(writableCredential, entry, hwInfo.dataChunkSize,
-                                         encryptedBlobs, true));
-    }
-
-    vector<uint8_t> credentialData;
-    vector<uint8_t> proofOfProvisioningSignature;
-    ASSERT_TRUE(
-            writableCredential->finishAddingEntries(&credentialData, &proofOfProvisioningSignature)
-                    .isOk());
-
-    // Validate the proofOfProvisioning which was returned
-    optional<vector<uint8_t>> proofOfProvisioning =
-            support::coseSignGetPayload(proofOfProvisioningSignature);
-    ASSERT_TRUE(proofOfProvisioning);
-    cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
-    EXPECT_EQ(
-            "[\n"
-            "  'ProofOfProvisioning',\n"
-            "  'org.iso.18013-5.2019.mdl',\n"
-            "  [\n"
-            "    {\n"
-            "      'id' : 0,\n"
-            "      'readerCertificate' : <not printed>,\n"
-            "    },\n"
-            "    {\n"
-            "      'id' : 1,\n"
-            "    },\n"
-            "  ],\n"
-            "  {\n"
-            "    'PersonalData' : [\n"
-            "      {\n"
-            "        'name' : 'Last name',\n"
-            "        'value' : 'Turing',\n"
-            "        'accessControlProfiles' : [0, 1, ],\n"
-            "      },\n"
-            "      {\n"
-            "        'name' : 'Birth date',\n"
-            "        'value' : '19120623',\n"
-            "        'accessControlProfiles' : [0, 1, ],\n"
-            "      },\n"
-            "      {\n"
-            "        'name' : 'First name',\n"
-            "        'value' : 'Alan',\n"
-            "        'accessControlProfiles' : [0, 1, ],\n"
-            "      },\n"
-            "      {\n"
-            "        'name' : 'Home address',\n"
-            "        'value' : 'Maida Vale, London, England',\n"
-            "        'accessControlProfiles' : [0, ],\n"
-            "      },\n"
-            "    ],\n"
-            "    'Image' : [\n"
-            "      {\n"
-            "        'name' : 'Portrait image',\n"
-            "        'value' : <bstr size=262134 sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
-            "        'accessControlProfiles' : [0, 1, ],\n"
-            "      },\n"
-            "    ],\n"
-            "  },\n"
-            "  true,\n"
-            "]",
-            cborPretty);
-
-    optional<vector<uint8_t>> credentialPubKey = support::certificateChainGetTopMostKey(
-            attData.attestationCertificate[0].encodedCertificate);
-    ASSERT_TRUE(credentialPubKey);
-    EXPECT_TRUE(support::coseCheckEcDsaSignature(proofOfProvisioningSignature,
-                                                 {},  // Additional data
-                                                 credentialPubKey.value()));
-    writableCredential = nullptr;
-
-    // Extract doctype, storage key, and credentialPrivKey from credentialData... this works
-    // only because we asked for a test-credential meaning that the HBK is all zeroes.
-    auto [exSuccess, exDocType, exStorageKey, exCredentialPrivKey] =
-            extractFromTestCredentialData(credentialData);
-    ASSERT_TRUE(exSuccess);
-    ASSERT_EQ(exDocType, "org.iso.18013-5.2019.mdl");
-    // ... check that the public key derived from the private key matches what was
-    // in the certificate.
-    optional<vector<uint8_t>> exCredentialKeyPair =
-            support::ecPrivateKeyToKeyPair(exCredentialPrivKey);
-    ASSERT_TRUE(exCredentialKeyPair);
-    optional<vector<uint8_t>> exCredentialPubKey =
-            support::ecKeyPairGetPublicKey(exCredentialKeyPair.value());
-    ASSERT_TRUE(exCredentialPubKey);
-    ASSERT_EQ(exCredentialPubKey.value(), credentialPubKey.value());
-
-    // Now that the credential has been provisioned, read it back and check the
-    // correct data is returned.
-    sp<IIdentityCredential> credential;
-    ASSERT_TRUE(credentialStore_
-                        ->getCredential(
-                                CipherSuite::CIPHERSUITE_ECDHE_HKDF_ECDSA_WITH_AES_256_GCM_SHA256,
-                                credentialData, &credential)
-                        .isOk());
-    ASSERT_NE(credential, nullptr);
-
-    optional<vector<uint8_t>> readerEphemeralKeyPair = support::createEcKeyPair();
-    ASSERT_TRUE(readerEphemeralKeyPair);
-    optional<vector<uint8_t>> readerEphemeralPublicKey =
-            support::ecKeyPairGetPublicKey(readerEphemeralKeyPair.value());
-    ASSERT_TRUE(credential->setReaderEphemeralPublicKey(readerEphemeralPublicKey.value()).isOk());
-
-    vector<uint8_t> ephemeralKeyPair;
-    ASSERT_TRUE(credential->createEphemeralKeyPair(&ephemeralKeyPair).isOk());
-    optional<vector<uint8_t>> ephemeralPublicKey = support::ecKeyPairGetPublicKey(ephemeralKeyPair);
-
-    // Calculate requestData field and sign it with the reader key.
-    auto [getXYSuccess, ephX, ephY] = support::ecPublicKeyGetXandY(ephemeralPublicKey.value());
-    ASSERT_TRUE(getXYSuccess);
-    cppbor::Map deviceEngagement = cppbor::Map().add("ephX", ephX).add("ephY", ephY);
-    vector<uint8_t> deviceEngagementBytes = deviceEngagement.encode();
-    vector<uint8_t> eReaderPubBytes = cppbor::Tstr("ignored").encode();
-    cppbor::Array sessionTranscript = cppbor::Array()
-                                              .add(cppbor::Semantic(24, deviceEngagementBytes))
-                                              .add(cppbor::Semantic(24, eReaderPubBytes));
-    vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
-
-    vector<uint8_t> itemsRequestBytes =
-            cppbor::Map("nameSpaces",
-                        cppbor::Map()
-                                .add("PersonalData", cppbor::Map()
-                                                             .add("Last name", false)
-                                                             .add("Birth date", false)
-                                                             .add("First name", false)
-                                                             .add("Home address", true))
-                                .add("Image", cppbor::Map().add("Portrait image", false)))
-                    .encode();
-    cborPretty = support::cborPrettyPrint(itemsRequestBytes, 32, {"EphemeralPublicKey"});
-    EXPECT_EQ(
-            "{\n"
-            "  'nameSpaces' : {\n"
-            "    'PersonalData' : {\n"
-            "      'Last name' : false,\n"
-            "      'Birth date' : false,\n"
-            "      'First name' : false,\n"
-            "      'Home address' : true,\n"
-            "    },\n"
-            "    'Image' : {\n"
-            "      'Portrait image' : false,\n"
-            "    },\n"
-            "  },\n"
-            "}",
-            cborPretty);
-    vector<uint8_t> encodedReaderAuthentication =
-            cppbor::Array()
-                    .add("ReaderAuthentication")
-                    .add(sessionTranscript.clone())
-                    .add(cppbor::Semantic(24, itemsRequestBytes))
-                    .encode();
-    vector<uint8_t> encodedReaderAuthenticationBytes =
-            cppbor::Semantic(24, encodedReaderAuthentication).encode();
-    optional<vector<uint8_t>> readerSignature =
-            support::coseSignEcDsa(readerKey, {},                     // content
-                                   encodedReaderAuthenticationBytes,  // detached content
-                                   readerCertificate.value());
-    ASSERT_TRUE(readerSignature);
-
-    // Generate the key that will be used to sign AuthenticatedData.
-    vector<uint8_t> signingKeyBlob;
-    Certificate signingKeyCertificate;
-    ASSERT_TRUE(credential->generateSigningKeyPair(&signingKeyBlob, &signingKeyCertificate).isOk());
-    optional<vector<uint8_t>> signingPubKey =
-            support::certificateChainGetTopMostKey(signingKeyCertificate.encodedCertificate);
-    EXPECT_TRUE(signingPubKey);
-    test_utils::verifyAuthKeyCertificate(signingKeyCertificate.encodedCertificate);
-
-    // Since we're using a test-credential we know storageKey meaning we can get the
-    // private key. Do this, derive the public key from it, and check this matches what
-    // is in the certificate...
-    const vector<uint8_t> exDocTypeVec(exDocType.begin(), exDocType.end());
-    optional<vector<uint8_t>> exSigningPrivKey =
-            support::decryptAes128Gcm(exStorageKey, signingKeyBlob, exDocTypeVec);
-    ASSERT_TRUE(exSigningPrivKey);
-    optional<vector<uint8_t>> exSigningKeyPair =
-            support::ecPrivateKeyToKeyPair(exSigningPrivKey.value());
-    ASSERT_TRUE(exSigningKeyPair);
-    optional<vector<uint8_t>> exSigningPubKey =
-            support::ecKeyPairGetPublicKey(exSigningKeyPair.value());
-    ASSERT_TRUE(exSigningPubKey);
-    ASSERT_EQ(exSigningPubKey.value(), signingPubKey.value());
-
-    vector<RequestNamespace> requestedNamespaces = test_utils::buildRequestNamespaces(testEntries);
-    // OK to fail, not available in v1 HAL
-    credential->setRequestedNamespaces(requestedNamespaces);
-    // OK to fail, not available in v1 HAL
-    credential->setVerificationToken(verificationToken);
-    ASSERT_TRUE(credential
-                        ->startRetrieval(secureProfiles.value(), authToken, itemsRequestBytes,
-                                         signingKeyBlob, sessionTranscriptEncoded,
-                                         readerSignature.value(), testEntriesEntryCounts)
-                        .isOk());
-
-    for (const auto& entry : testEntries) {
-        ASSERT_TRUE(credential
-                            ->startRetrieveEntryValue(entry.nameSpace, entry.name,
-                                                      entry.valueCbor.size(), entry.profileIds)
-                            .isOk());
-
-        auto it = encryptedBlobs.find(&entry);
-        ASSERT_NE(it, encryptedBlobs.end());
-        const vector<vector<uint8_t>>& encryptedChunks = it->second;
-
-        vector<uint8_t> content;
-        for (const auto& encryptedChunk : encryptedChunks) {
-            vector<uint8_t> chunk;
-            ASSERT_TRUE(credential->retrieveEntryValue(encryptedChunk, &chunk).isOk());
-            content.insert(content.end(), chunk.begin(), chunk.end());
-        }
-        EXPECT_EQ(content, entry.valueCbor);
-
-        // TODO: also use |exStorageKey| to decrypt data and check it's the same as whatt
-        // the HAL returns...
-    }
-
-    vector<uint8_t> mac;
-    vector<uint8_t> deviceNameSpacesEncoded;
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
-    ASSERT_EQ(
-            "{\n"
-            "  'PersonalData' : {\n"
-            "    'Last name' : 'Turing',\n"
-            "    'Birth date' : '19120623',\n"
-            "    'First name' : 'Alan',\n"
-            "    'Home address' : 'Maida Vale, London, England',\n"
-            "  },\n"
-            "  'Image' : {\n"
-            "    'Portrait image' : <bstr size=262134 "
-            "sha1=941e372f654d86c32d88fae9e41b706afbfd02bb>,\n"
-            "  },\n"
-            "}",
-            cborPretty);
-
-    string docType = "org.iso.18013-5.2019.mdl";
-    optional<vector<uint8_t>> readerEphemeralPrivateKey =
-            support::ecKeyPairGetPrivateKey(readerEphemeralKeyPair.value());
-    optional<vector<uint8_t>> eMacKey = support::calcEMacKey(
-            readerEphemeralPrivateKey.value(),                           // Private Key
-            signingPubKey.value(),                                       // Public Key
-            cppbor::Semantic(24, sessionTranscript.encode()).encode());  // SessionTranscriptBytes
-    optional<vector<uint8_t>> calculatedMac =
-            support::calcMac(sessionTranscript.encode(),  // SessionTranscript
-                             docType,                     // DocType
-                             deviceNameSpacesEncoded,     // DeviceNamespaces
-                             eMacKey.value());            // EMacKey
-    ASSERT_TRUE(calculatedMac);
-    EXPECT_EQ(mac, calculatedMac);
-
-    // Also perform an additional empty request. This is what mDL applications
-    // are envisioned to do - one call to get the data elements, another to get
-    // an empty DeviceSignedItems and corresponding MAC.
-    //
-    credential->setRequestedNamespaces({});  // OK to fail, not available in v1 HAL
-    ASSERT_TRUE(credential
-                        ->startRetrieval(
-                                secureProfiles.value(), authToken, {},         // itemsRequestBytes
-                                signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
-                                testEntriesEntryCounts)
-                        .isOk());
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
-    ASSERT_EQ("{}", cborPretty);
-    // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
-    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
-                                     docType,                     // DocType
-                                     deviceNameSpacesEncoded,     // DeviceNamespaces
-                                     eMacKey.value());            // EMacKey
-    ASSERT_TRUE(calculatedMac);
-    EXPECT_EQ(mac, calculatedMac);
-
-    // Some mDL apps might send a request but with a single empty
-    // namespace. Check that too.
-    RequestNamespace emptyRequestNS;
-    emptyRequestNS.namespaceName = "PersonalData";
-    credential->setRequestedNamespaces({emptyRequestNS});  // OK to fail, not available in v1 HAL
-    ASSERT_TRUE(credential
-                        ->startRetrieval(
-                                secureProfiles.value(), authToken, {},         // itemsRequestBytes
-                                signingKeyBlob, sessionTranscriptEncoded, {},  // readerSignature,
-                                testEntriesEntryCounts)
-                        .isOk());
-    ASSERT_TRUE(credential->finishRetrieval(&mac, &deviceNameSpacesEncoded).isOk());
-    cborPretty = support::cborPrettyPrint(deviceNameSpacesEncoded, 32, {});
-    ASSERT_EQ("{}", cborPretty);
-    // Calculate DeviceAuthentication and MAC (MACing key hasn't changed)
-    calculatedMac = support::calcMac(sessionTranscript.encode(),  // SessionTranscript
-                                     docType,                     // DocType
-                                     deviceNameSpacesEncoded,     // DeviceNamespaces
-                                     eMacKey.value());            // EMacKey
-    ASSERT_TRUE(calculatedMac);
-    EXPECT_EQ(mac, calculatedMac);
-}
-
-INSTANTIATE_TEST_SUITE_P(
-        Identity, IdentityAidl,
-        testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
-        android::PrintInstanceNameToString);
-// INSTANTIATE_TEST_SUITE_P(Identity, IdentityAidl,
-// testing::Values("android.hardware.identity.IIdentityCredentialStore/default"));
-
-}  // namespace android::hardware::identity
-
-int main(int argc, char** argv) {
-    ::testing::InitGoogleTest(&argc, argv);
-    ::android::ProcessState::self()->setThreadPoolMaxThreadCount(1);
-    ::android::ProcessState::self()->startThreadPool();
-    return RUN_ALL_TESTS();
-}
diff --git a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
index 5435ed8..bc37020 100644
--- a/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
+++ b/identity/aidl/vts/VtsIWritableIdentityCredentialTests.cpp
@@ -29,7 +29,7 @@
 #include <future>
 #include <map>
 
-#include "VtsIdentityTestUtils.h"
+#include "Util.h"
 
 namespace android::hardware::identity {
 
@@ -182,7 +182,7 @@
                                                     false /* testCredential */));
 
     // Verify set a large number of profile count and entry count is ok
-    const vector<int32_t> entryCounts = {3000};
+    const vector<int32_t> entryCounts = {255};
     writableCredential->setExpectedProofOfProvisioningSize(123456);
     result = writableCredential->startPersonalization(25, entryCounts);
     EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
@@ -338,8 +338,7 @@
     optional<vector<uint8_t>> proofOfProvisioning =
             support::coseSignGetPayload(proofOfProvisioningSignature);
     ASSERT_TRUE(proofOfProvisioning);
-    string cborPretty =
-            support::cborPrettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(), 32, {"readerCertificate"});
     EXPECT_EQ(
             "[\n"
             "  'ProofOfProvisioning',\n"
@@ -449,9 +448,9 @@
     optional<vector<uint8_t>> proofOfProvisioning =
             support::coseSignGetPayload(proofOfProvisioningSignature);
     ASSERT_TRUE(proofOfProvisioning);
-    string cborPretty = support::cborPrettyPrint(proofOfProvisioning.value(),
-                                                 32,  //
-                                                 {"readerCertificate"});
+    string cborPretty = cppbor::prettyPrint(proofOfProvisioning.value(),
+                                            32,  //
+                                            {"readerCertificate"});
     EXPECT_EQ(
             "[\n"
             "  'ProofOfProvisioning',\n"
@@ -717,6 +716,7 @@
     ASSERT_EQ(IIdentityCredentialStore::STATUS_INVALID_DATA, result.serviceSpecificErrorCode());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(IdentityCredentialTests);
 INSTANTIATE_TEST_SUITE_P(
         Identity, IdentityCredentialTests,
         testing::ValuesIn(android::getAidlHalInstanceNames(IIdentityCredentialStore::descriptor)),
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.cpp b/identity/aidl/vts/VtsIdentityTestUtils.cpp
deleted file mode 100644
index 3b10651..0000000
--- a/identity/aidl/vts/VtsIdentityTestUtils.cpp
+++ /dev/null
@@ -1,472 +0,0 @@
-/*
- * Copyright 2019, 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.
- */
-
-#define LOG_TAG "VtsIdentityTestUtils"
-
-#include "VtsIdentityTestUtils.h"
-
-#include <aidl/Gtest.h>
-#include <android-base/logging.h>
-#include <keymaster/km_openssl/openssl_utils.h>
-#include <keymasterV4_1/attestation_record.h>
-#include <charconv>
-#include <map>
-
-namespace android::hardware::identity::test_utils {
-
-using std::endl;
-using std::map;
-using std::optional;
-using std::string;
-using std::vector;
-
-using ::android::sp;
-using ::android::String16;
-using ::android::binder::Status;
-using ::keymaster::X509_Ptr;
-
-bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
-                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential) {
-    if (credentialStore == nullptr) {
-        return false;
-    }
-
-    string docType = "org.iso.18013-5.2019.mdl";
-    Status result = credentialStore->createCredential(docType, testCredential, &writableCredential);
-
-    if (result.isOk() && writableCredential != nullptr) {
-        return true;
-    } else {
-        return false;
-    }
-}
-
-optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal) {
-    vector<uint8_t> privKey;
-    return generateReaderCertificate(serialDecimal, &privKey);
-}
-
-optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
-                                                    vector<uint8_t>* outReaderPrivateKey) {
-    optional<vector<uint8_t>> readerKeyPKCS8 = support::createEcKeyPair();
-    if (!readerKeyPKCS8) {
-        return {};
-    }
-
-    optional<vector<uint8_t>> readerPublicKey =
-            support::ecKeyPairGetPublicKey(readerKeyPKCS8.value());
-    optional<vector<uint8_t>> readerKey = support::ecKeyPairGetPrivateKey(readerKeyPKCS8.value());
-    if (!readerPublicKey || !readerKey) {
-        return {};
-    }
-
-    if (outReaderPrivateKey == nullptr) {
-        return {};
-    }
-
-    *outReaderPrivateKey = readerKey.value();
-
-    string issuer = "Android Open Source Project";
-    string subject = "Android IdentityCredential VTS Test";
-    time_t validityNotBefore = time(nullptr);
-    time_t validityNotAfter = validityNotBefore + 365 * 24 * 3600;
-
-    return support::ecPublicKeyGenerateCertificate(readerPublicKey.value(), readerKey.value(),
-                                                   serialDecimal, issuer, subject,
-                                                   validityNotBefore, validityNotAfter);
-}
-
-optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
-        sp<IWritableIdentityCredential>& writableCredential,
-        const vector<TestProfile>& testProfiles) {
-    Status result;
-
-    vector<SecureAccessControlProfile> secureProfiles;
-
-    for (const auto& testProfile : testProfiles) {
-        SecureAccessControlProfile profile;
-        Certificate cert;
-        cert.encodedCertificate = testProfile.readerCertificate;
-        int64_t secureUserId = testProfile.userAuthenticationRequired ? 66 : 0;
-        result = writableCredential->addAccessControlProfile(
-                testProfile.id, cert, testProfile.userAuthenticationRequired,
-                testProfile.timeoutMillis, secureUserId, &profile);
-
-        // Don't use assert so all errors can be outputed.  Then return
-        // instead of exit even on errors so caller can decide.
-        EXPECT_TRUE(result.isOk()) << result.exceptionCode() << "; " << result.exceptionMessage()
-                                   << "test profile id = " << testProfile.id << endl;
-        EXPECT_EQ(testProfile.id, profile.id);
-        EXPECT_EQ(testProfile.readerCertificate, profile.readerCertificate.encodedCertificate);
-        EXPECT_EQ(testProfile.userAuthenticationRequired, profile.userAuthenticationRequired);
-        EXPECT_EQ(testProfile.timeoutMillis, profile.timeoutMillis);
-        EXPECT_EQ(support::kAesGcmTagSize + support::kAesGcmIvSize, profile.mac.size());
-
-        if (!result.isOk() || testProfile.id != profile.id ||
-            testProfile.readerCertificate != profile.readerCertificate.encodedCertificate ||
-            testProfile.userAuthenticationRequired != profile.userAuthenticationRequired ||
-            testProfile.timeoutMillis != profile.timeoutMillis ||
-            support::kAesGcmTagSize + support::kAesGcmIvSize != profile.mac.size()) {
-            return {};
-        }
-
-        secureProfiles.push_back(profile);
-    }
-
-    return secureProfiles;
-}
-
-// Most test expects this function to pass. So we will print out additional
-// value if failed so more debug data can be provided.
-bool addEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
-              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
-              bool expectSuccess) {
-    Status result;
-    vector<vector<uint8_t>> chunks = support::chunkVector(entry.valueCbor, dataChunkSize);
-
-    result = writableCredential->beginAddEntry(entry.profileIds, entry.nameSpace, entry.name,
-                                               entry.valueCbor.size());
-
-    if (expectSuccess) {
-        EXPECT_TRUE(result.isOk())
-                << result.exceptionCode() << "; " << result.exceptionMessage() << endl
-                << "entry name = " << entry.name << ", name space=" << entry.nameSpace << endl;
-    }
-
-    if (!result.isOk()) {
-        return false;
-    }
-
-    vector<vector<uint8_t>> encryptedChunks;
-    for (const auto& chunk : chunks) {
-        vector<uint8_t> encryptedContent;
-        result = writableCredential->addEntryValue(chunk, &encryptedContent);
-        if (expectSuccess) {
-            EXPECT_TRUE(result.isOk())
-                    << result.exceptionCode() << "; " << result.exceptionMessage() << endl
-                    << "entry name = " << entry.name << ", name space = " << entry.nameSpace
-                    << endl;
-
-            EXPECT_GT(encryptedContent.size(), 0u) << "entry name = " << entry.name
-                                                   << ", name space = " << entry.nameSpace << endl;
-        }
-
-        if (!result.isOk() || encryptedContent.size() <= 0u) {
-            return false;
-        }
-
-        encryptedChunks.push_back(encryptedContent);
-    }
-
-    encryptedBlobs[&entry] = encryptedChunks;
-    return true;
-}
-
-void setImageData(vector<uint8_t>& image) {
-    image.resize(256 * 1024 - 10);
-    for (size_t n = 0; n < image.size(); n++) {
-        image[n] = (uint8_t)n;
-    }
-}
-
-string x509NameToRfc2253String(X509_NAME* name) {
-    char* buf;
-    size_t bufSize;
-    BIO* bio;
-
-    bio = BIO_new(BIO_s_mem());
-    X509_NAME_print_ex(bio, name, 0, XN_FLAG_RFC2253);
-    bufSize = BIO_get_mem_data(bio, &buf);
-    string ret = string(buf, bufSize);
-    BIO_free(bio);
-
-    return ret;
-}
-
-int parseDigits(const char** s, int numDigits) {
-    int result;
-    auto [_, ec] = std::from_chars(*s, *s + numDigits, result);
-    if (ec != std::errc()) {
-        LOG(ERROR) << "Error parsing " << numDigits << " digits "
-                   << " from " << s;
-        return 0;
-    }
-    *s += numDigits;
-    return result;
-}
-
-bool parseAsn1Time(const ASN1_TIME* asn1Time, time_t* outTime) {
-    struct tm tm;
-
-    memset(&tm, '\0', sizeof(tm));
-    const char* timeStr = (const char*)asn1Time->data;
-    const char* s = timeStr;
-    if (asn1Time->type == V_ASN1_UTCTIME) {
-        tm.tm_year = parseDigits(&s, 2);
-        if (tm.tm_year < 70) {
-            tm.tm_year += 100;
-        }
-    } else if (asn1Time->type == V_ASN1_GENERALIZEDTIME) {
-        tm.tm_year = parseDigits(&s, 4) - 1900;
-        tm.tm_year -= 1900;
-    } else {
-        LOG(ERROR) << "Unsupported ASN1_TIME type " << asn1Time->type;
-        return false;
-    }
-    tm.tm_mon = parseDigits(&s, 2) - 1;
-    tm.tm_mday = parseDigits(&s, 2);
-    tm.tm_hour = parseDigits(&s, 2);
-    tm.tm_min = parseDigits(&s, 2);
-    tm.tm_sec = parseDigits(&s, 2);
-    // This may need to be updated if someone create certificates using +/- instead of Z.
-    //
-    if (*s != 'Z') {
-        LOG(ERROR) << "Expected Z in string '" << timeStr << "' at offset " << (s - timeStr);
-        return false;
-    }
-
-    time_t t = timegm(&tm);
-    if (t == -1) {
-        LOG(ERROR) << "Error converting broken-down time to time_t";
-        return false;
-    }
-    *outTime = t;
-    return true;
-}
-
-void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
-                                    const vector<uint8_t>& expectedChallenge,
-                                    const vector<uint8_t>& expectedAppId, bool isTestCredential) {
-    ASSERT_GE(credentialKeyCertChain.size(), 2);
-
-    vector<uint8_t> certBytes = credentialKeyCertChain[0].encodedCertificate;
-    const uint8_t* certData = certBytes.data();
-    X509_Ptr cert = X509_Ptr(d2i_X509(nullptr, &certData, certBytes.size()));
-
-    vector<uint8_t> batchCertBytes = credentialKeyCertChain[1].encodedCertificate;
-    const uint8_t* batchCertData = batchCertBytes.data();
-    X509_Ptr batchCert = X509_Ptr(d2i_X509(nullptr, &batchCertData, batchCertBytes.size()));
-
-    // First get some values from the batch certificate which is checked
-    // against the top-level certificate (subject, notAfter)
-    //
-
-    X509_NAME* batchSubject = X509_get_subject_name(batchCert.get());
-    ASSERT_NE(nullptr, batchSubject);
-    time_t batchNotAfter;
-    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(batchCert.get()), &batchNotAfter));
-
-    // Check all the requirements from IWritableIdentityCredential::getAttestationCertificate()...
-    //
-
-    //  - version: INTEGER 2 (means v3 certificate).
-    EXPECT_EQ(2, X509_get_version(cert.get()));
-
-    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
-    EXPECT_EQ(1, ASN1_INTEGER_get(X509_get_serialNumber(cert.get())));
-
-    //  - signature: must be set to ECDSA.
-    EXPECT_EQ(NID_ecdsa_with_SHA256, X509_get_signature_nid(cert.get()));
-
-    //  - subject: CN shall be set to "Android Identity Credential Key". (fixed value:
-    //    same on all certs)
-    X509_NAME* subject = X509_get_subject_name(cert.get());
-    ASSERT_NE(nullptr, subject);
-    EXPECT_EQ("CN=Android Identity Credential Key", x509NameToRfc2253String(subject));
-
-    //  - issuer: Same as the subject field of the batch attestation key.
-    X509_NAME* issuer = X509_get_issuer_name(cert.get());
-    ASSERT_NE(nullptr, issuer);
-    EXPECT_EQ(x509NameToRfc2253String(batchSubject), x509NameToRfc2253String(issuer));
-
-    //  - validity: Should be from current time and expire at the same time as the
-    //    attestation batch certificate used.
-    //
-    //  Allow for 10 seconds drift to account for the time drift between Secure HW
-    //  and this environment plus the difference between when the certificate was
-    //  created and until now
-    //
-    time_t notBefore;
-    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
-    uint64_t now = time(nullptr);
-    int64_t diffSecs = now - notBefore;
-    int64_t allowDriftSecs = 10;
-    EXPECT_LE(-allowDriftSecs, diffSecs);
-    EXPECT_GE(allowDriftSecs, diffSecs);
-
-    time_t notAfter;
-    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
-    EXPECT_EQ(notAfter, batchNotAfter);
-
-    auto [err, attRec] = keymaster::V4_1::parse_attestation_record(certBytes);
-    ASSERT_EQ(keymaster::V4_1::ErrorCode::OK, err);
-
-    //  - subjectPublicKeyInfo: must contain attested public key.
-
-    //  - The attestationVersion field in the attestation extension must be at least 3.
-    EXPECT_GE(attRec.attestation_version, 3);
-
-    //  - The attestationSecurityLevel field must be set to either Software (0),
-    //    TrustedEnvironment (1), or StrongBox (2) depending on how attestation is
-    //    implemented.
-    EXPECT_GE(attRec.attestation_security_level,
-              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
-
-    //  - The keymasterVersion field in the attestation extension must be set to the.
-    //    same value as used for Android Keystore keys.
-    //
-    // Nothing to check here...
-
-    //  - The keymasterSecurityLevel field in the attestation extension must be set to
-    //    either Software (0), TrustedEnvironment (1), or StrongBox (2) depending on how
-    //    the Trusted Application backing the HAL implementation is implemented.
-    EXPECT_GE(attRec.keymaster_security_level, keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
-
-    //  - The attestationChallenge field must be set to the passed-in challenge.
-    EXPECT_EQ(expectedChallenge.size(), attRec.attestation_challenge.size());
-    EXPECT_TRUE(memcmp(expectedChallenge.data(), attRec.attestation_challenge.data(),
-                       attRec.attestation_challenge.size()) == 0);
-
-    //  - The uniqueId field must be empty.
-    EXPECT_EQ(attRec.unique_id.size(), 0);
-
-    //  - The softwareEnforced field in the attestation extension must include
-    //    Tag::ATTESTATION_APPLICATION_ID which must be set to the bytes of the passed-in
-    //    attestationApplicationId.
-    EXPECT_TRUE(attRec.software_enforced.Contains(keymaster::V4_0::TAG_ATTESTATION_APPLICATION_ID,
-                                                  expectedAppId));
-
-    //  - The teeEnforced field in the attestation extension must include
-    //
-    //    - Tag::IDENTITY_CREDENTIAL_KEY which indicates that the key is an Identity
-    //      Credential key (which can only sign/MAC very specific messages) and not an Android
-    //      Keystore key (which can be used to sign/MAC anything). This must not be set
-    //      for test credentials.
-    bool hasIcKeyTag =
-            attRec.hardware_enforced.Contains(static_cast<android::hardware::keymaster::V4_0::Tag>(
-                    keymaster::V4_1::Tag::IDENTITY_CREDENTIAL_KEY));
-    if (isTestCredential) {
-        EXPECT_FALSE(hasIcKeyTag);
-    } else {
-        EXPECT_TRUE(hasIcKeyTag);
-    }
-
-    //    - Tag::PURPOSE must be set to SIGN
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_PURPOSE,
-                                                  keymaster::V4_0::KeyPurpose::SIGN));
-
-    //    - Tag::KEY_SIZE must be set to the appropriate key size, in bits (e.g. 256)
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_KEY_SIZE, 256));
-
-    //    - Tag::ALGORITHM must be set to EC
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_ALGORITHM,
-                                                  keymaster::V4_0::Algorithm::EC));
-
-    //    - Tag::NO_AUTH_REQUIRED must be set
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_NO_AUTH_REQUIRED));
-
-    //    - Tag::DIGEST must be include SHA_2_256
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_DIGEST,
-                                                  keymaster::V4_0::Digest::SHA_2_256));
-
-    //    - Tag::EC_CURVE must be set to P_256
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_EC_CURVE,
-                                                  keymaster::V4_0::EcCurve::P_256));
-
-    //    - Tag::ROOT_OF_TRUST must be set
-    //
-    EXPECT_GE(attRec.root_of_trust.security_level,
-              keymaster::V4_0::SecurityLevel::TRUSTED_ENVIRONMENT);
-
-    //    - Tag::OS_VERSION and Tag::OS_PATCHLEVEL must be set
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_VERSION));
-    EXPECT_TRUE(attRec.hardware_enforced.Contains(keymaster::V4_0::TAG_OS_PATCHLEVEL));
-
-    // TODO: we could retrieve osVersion and osPatchLevel from Android itself and compare it
-    // with what was reported in the certificate.
-}
-
-void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain) {
-    const uint8_t* data = authKeyCertChain.data();
-    auto cert = X509_Ptr(d2i_X509(nullptr, &data, authKeyCertChain.size()));
-
-    //  - version: INTEGER 2 (means v3 certificate).
-    EXPECT_EQ(X509_get_version(cert.get()), 2);
-
-    //  - serialNumber: INTEGER 1 (fixed value: same on all certs).
-    EXPECT_EQ(ASN1_INTEGER_get(X509_get_serialNumber(cert.get())), 1);
-
-    //  - signature: must be set to ECDSA.
-    EXPECT_EQ(X509_get_signature_nid(cert.get()), NID_ecdsa_with_SHA256);
-
-    //  - subject: CN shall be set to "Android Identity Credential Authentication Key". (fixed
-    //    value: same on all certs)
-    X509_NAME* subject = X509_get_subject_name(cert.get());
-    ASSERT_NE(subject, nullptr);
-    EXPECT_EQ(x509NameToRfc2253String(subject),
-              "CN=Android Identity Credential Authentication Key");
-
-    //  - issuer: CN shall be set to "Android Identity Credential Key". (fixed value:
-    //    same on all certs)
-    X509_NAME* issuer = X509_get_issuer_name(cert.get());
-    ASSERT_NE(issuer, nullptr);
-    EXPECT_EQ(x509NameToRfc2253String(issuer), "CN=Android Identity Credential Key");
-
-    //  - subjectPublicKeyInfo: must contain attested public key.
-
-    //  - validity: should be from current time and one year in the future (365 days).
-    time_t notBefore, notAfter;
-    ASSERT_TRUE(parseAsn1Time(X509_get0_notAfter(cert.get()), &notAfter));
-    ASSERT_TRUE(parseAsn1Time(X509_get0_notBefore(cert.get()), &notBefore));
-
-    //  Allow for 10 seconds drift to account for the time drift between Secure HW
-    //  and this environment plus the difference between when the certificate was
-    //  created and until now
-    //
-    uint64_t now = time(nullptr);
-    int64_t diffSecs = now - notBefore;
-    int64_t allowDriftSecs = 10;
-    EXPECT_LE(-allowDriftSecs, diffSecs);
-    EXPECT_GE(allowDriftSecs, diffSecs);
-    constexpr uint64_t kSecsInOneYear = 365 * 24 * 60 * 60;
-    EXPECT_EQ(notBefore + kSecsInOneYear, notAfter);
-}
-
-vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries) {
-    vector<RequestNamespace> ret;
-    RequestNamespace curNs;
-    for (const TestEntryData& testEntry : entries) {
-        if (testEntry.nameSpace != curNs.namespaceName) {
-            if (curNs.namespaceName.size() > 0) {
-                ret.push_back(curNs);
-            }
-            curNs.namespaceName = testEntry.nameSpace;
-            curNs.items.clear();
-        }
-
-        RequestDataItem item;
-        item.name = testEntry.name;
-        item.size = testEntry.valueCbor.size();
-        item.accessControlProfileIds = testEntry.profileIds;
-        curNs.items.push_back(item);
-    }
-    if (curNs.namespaceName.size() > 0) {
-        ret.push_back(curNs);
-    }
-    return ret;
-}
-
-}  // namespace android::hardware::identity::test_utils
diff --git a/identity/aidl/vts/VtsIdentityTestUtils.h b/identity/aidl/vts/VtsIdentityTestUtils.h
deleted file mode 100644
index 85c24f8..0000000
--- a/identity/aidl/vts/VtsIdentityTestUtils.h
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright 2019, 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 VTS_IDENTITY_TEST_UTILS_H
-#define VTS_IDENTITY_TEST_UTILS_H
-
-#include <android/hardware/identity/IIdentityCredentialStore.h>
-#include <android/hardware/identity/support/IdentityCredentialSupport.h>
-#include <cppbor.h>
-#include <cppbor_parse.h>
-
-namespace android::hardware::identity::test_utils {
-
-using ::std::map;
-using ::std::optional;
-using ::std::string;
-using ::std::vector;
-
-using ::android::sp;
-using ::android::binder::Status;
-
-struct AttestationData {
-    AttestationData(sp<IWritableIdentityCredential>& writableCredential, string challenge,
-                    vector<uint8_t> attestationAppId)
-        : attestationApplicationId(attestationAppId) {
-        // ASSERT_NE(writableCredential, nullptr);
-
-        if (!challenge.empty()) {
-            attestationChallenge.assign(challenge.begin(), challenge.end());
-        }
-
-        result = writableCredential->getAttestationCertificate(
-                attestationApplicationId, attestationChallenge, &attestationCertificate);
-    }
-
-    AttestationData() {}
-
-    vector<uint8_t> attestationChallenge;
-    vector<uint8_t> attestationApplicationId;
-    vector<Certificate> attestationCertificate;
-    Status result;
-};
-
-struct TestEntryData {
-    TestEntryData(string nameSpace, string name, vector<int32_t> profileIds)
-        : nameSpace(nameSpace), name(name), profileIds(profileIds) {}
-
-    TestEntryData(string nameSpace, string name, const string& value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Tstr(((const char*)value.data())).encode();
-    }
-    TestEntryData(string nameSpace, string name, const vector<uint8_t>& value,
-                  vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Bstr(value).encode();
-    }
-    TestEntryData(string nameSpace, string name, bool value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        valueCbor = cppbor::Bool(value).encode();
-    }
-    TestEntryData(string nameSpace, string name, int64_t value, vector<int32_t> profileIds)
-        : TestEntryData(nameSpace, name, profileIds) {
-        if (value >= 0) {
-            valueCbor = cppbor::Uint(value).encode();
-        } else {
-            valueCbor = cppbor::Nint(-value).encode();
-        }
-    }
-
-    string nameSpace;
-    string name;
-    vector<uint8_t> valueCbor;
-    vector<int32_t> profileIds;
-};
-
-struct TestProfile {
-    uint16_t id;
-    vector<uint8_t> readerCertificate;
-    bool userAuthenticationRequired;
-    uint64_t timeoutMillis;
-};
-
-bool setupWritableCredential(sp<IWritableIdentityCredential>& writableCredential,
-                             sp<IIdentityCredentialStore>& credentialStore, bool testCredential);
-
-optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal);
-
-optional<vector<uint8_t>> generateReaderCertificate(string serialDecimal,
-                                                    vector<uint8_t>* outReaderPrivateKey);
-
-optional<vector<SecureAccessControlProfile>> addAccessControlProfiles(
-        sp<IWritableIdentityCredential>& writableCredential,
-        const vector<TestProfile>& testProfiles);
-
-bool addEntry(sp<IWritableIdentityCredential>& writableCredential, const TestEntryData& entry,
-              int dataChunkSize, map<const TestEntryData*, vector<vector<uint8_t>>>& encryptedBlobs,
-              bool expectSuccess);
-
-void setImageData(vector<uint8_t>& image);
-
-void validateAttestationCertificate(const vector<Certificate>& credentialKeyCertChain,
-                                    const vector<uint8_t>& expectedChallenge,
-                                    const vector<uint8_t>& expectedAppId, bool isTestCredential);
-
-vector<RequestNamespace> buildRequestNamespaces(const vector<TestEntryData> entries);
-
-// Verifies that the X.509 certificate for a just created authentication key
-// is valid.
-//
-void verifyAuthKeyCertificate(const vector<uint8_t>& authKeyCertChain);
-
-}  // namespace android::hardware::identity::test_utils
-
-#endif  // VTS_IDENTITY_TEST_UTILS_H
diff --git a/identity/support/Android.bp b/identity/support/Android.bp
index 2b6c695..db1a945 100644
--- a/identity/support/Android.bp
+++ b/identity/support/Android.bp
@@ -13,6 +13,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.identity-support-lib",
     vendor_available: true,
@@ -26,6 +35,7 @@
         "android.hardware.keymaster@4.0",
         "libcrypto",
         "libbase",
+        "libcppcose_rkp",
         "libhidlbase",
         "libhardware",
         "libkeymaster_portable",
@@ -33,7 +43,7 @@
         "libpuresoftkeymasterdevice",
     ],
     static_libs: [
-        "libcppbor",
+        "libcppbor_external",
     ],
 }
 
@@ -50,7 +60,7 @@
         "libhardware",
     ],
     static_libs: [
-        "libcppbor",
+        "libcppbor_external",
         "libgmock",
     ],
     test_suites: ["general-tests"],
@@ -80,7 +90,7 @@
         "tests/cppbor_test.cpp",
     ],
     shared_libs: [
-        "libcppbor",
+        "libcppbor_external",
         "libbase",
     ],
     static_libs: [
@@ -95,7 +105,7 @@
         "tests/cppbor_test.cpp",
     ],
     shared_libs: [
-        "libcppbor",
+        "libcppbor_external",
         "libbase",
     ],
     static_libs: [
diff --git a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
index 3aa5bb6..3b91de6 100644
--- a/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
+++ b/identity/support/include/android/hardware/identity/support/IdentityCredentialSupport.h
@@ -18,6 +18,7 @@
 #define IDENTITY_SUPPORT_INCLUDE_IDENTITY_CREDENTIAL_UTILS_H_
 
 #include <cstdint>
+#include <map>
 #include <optional>
 #include <string>
 #include <tuple>
@@ -29,11 +30,12 @@
 namespace identity {
 namespace support {
 
+using ::std::map;
 using ::std::optional;
+using ::std::pair;
 using ::std::string;
 using ::std::tuple;
 using ::std::vector;
-using ::std::pair;
 
 // The semantic tag for a bstr which includes Encoded CBOR (RFC 7049, section 2.4)
 const int kSemanticTagEncodedCbor = 24;
@@ -221,6 +223,11 @@
 //
 optional<pair<time_t, time_t>> certificateGetValidity(const vector<uint8_t>& x509Certificate);
 
+// Looks for an extension with OID in |oidStr| which must be an stored as an OCTET STRING.
+//
+optional<vector<uint8_t>> certificateGetExtension(const vector<uint8_t>& x509Certificate,
+                                                  const string& oidStr);
+
 // Generates a X.509 certificate for |publicKey| (which must be in the format
 // returned by ecKeyPairGetPublicKey()).
 //
@@ -230,7 +237,8 @@
 optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
         const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
         const string& serialDecimal, const string& issuer, const string& subject,
-        time_t validityNotBefore, time_t validityNotAfter);
+        time_t validityNotBefore, time_t validityNotAfter,
+        const map<string, vector<uint8_t>>& extensions);
 
 // Performs Elliptic-curve Diffie-Helman using |publicKey| (which must be in the
 // format returned by ecKeyPairGetPublicKey()) and |privateKey| (which must be
diff --git a/identity/support/src/IdentityCredentialSupport.cpp b/identity/support/src/IdentityCredentialSupport.cpp
index 08559c6..7f4674d 100644
--- a/identity/support/src/IdentityCredentialSupport.cpp
+++ b/identity/support/src/IdentityCredentialSupport.cpp
@@ -55,6 +55,7 @@
 #include <keymaster/contexts/soft_attestation_cert.h>
 #include <keymaster/keymaster_tags.h>
 #include <keymaster/km_openssl/attestation_utils.h>
+#include <keymaster/km_openssl/certificate_utils.h>
 
 namespace android {
 namespace hardware {
@@ -147,211 +148,25 @@
 }
 
 // ---------------------------------------------------------------------------
-// CBOR utilities.
-// ---------------------------------------------------------------------------
-
-static bool cborAreAllElementsNonCompound(const cppbor::CompoundItem* compoundItem) {
-    if (compoundItem->type() == cppbor::ARRAY) {
-        const cppbor::Array* array = compoundItem->asArray();
-        for (size_t n = 0; n < array->size(); n++) {
-            const cppbor::Item* entry = (*array)[n].get();
-            switch (entry->type()) {
-                case cppbor::ARRAY:
-                case cppbor::MAP:
-                    return false;
-                default:
-                    break;
-            }
-        }
-    } else {
-        const cppbor::Map* map = compoundItem->asMap();
-        for (size_t n = 0; n < map->size(); n++) {
-            auto [keyEntry, valueEntry] = (*map)[n];
-            switch (keyEntry->type()) {
-                case cppbor::ARRAY:
-                case cppbor::MAP:
-                    return false;
-                default:
-                    break;
-            }
-            switch (valueEntry->type()) {
-                case cppbor::ARRAY:
-                case cppbor::MAP:
-                    return false;
-                default:
-                    break;
-            }
-        }
-    }
-    return true;
-}
-
-static bool cborPrettyPrintInternal(const cppbor::Item* item, string& out, size_t indent,
-                                    size_t maxBStrSize, const vector<string>& mapKeysToNotPrint) {
-    char buf[80];
-
-    string indentString(indent, ' ');
-
-    switch (item->type()) {
-        case cppbor::UINT:
-            snprintf(buf, sizeof(buf), "%" PRIu64, item->asUint()->unsignedValue());
-            out.append(buf);
-            break;
-
-        case cppbor::NINT:
-            snprintf(buf, sizeof(buf), "%" PRId64, item->asNint()->value());
-            out.append(buf);
-            break;
-
-        case cppbor::BSTR: {
-            const cppbor::Bstr* bstr = item->asBstr();
-            const vector<uint8_t>& value = bstr->value();
-            if (value.size() > maxBStrSize) {
-                unsigned char digest[SHA_DIGEST_LENGTH];
-                SHA_CTX ctx;
-                SHA1_Init(&ctx);
-                SHA1_Update(&ctx, value.data(), value.size());
-                SHA1_Final(digest, &ctx);
-                char buf2[SHA_DIGEST_LENGTH * 2 + 1];
-                for (size_t n = 0; n < SHA_DIGEST_LENGTH; n++) {
-                    snprintf(buf2 + n * 2, 3, "%02x", digest[n]);
-                }
-                snprintf(buf, sizeof(buf), "<bstr size=%zd sha1=%s>", value.size(), buf2);
-                out.append(buf);
-            } else {
-                out.append("{");
-                for (size_t n = 0; n < value.size(); n++) {
-                    if (n > 0) {
-                        out.append(", ");
-                    }
-                    snprintf(buf, sizeof(buf), "0x%02x", value[n]);
-                    out.append(buf);
-                }
-                out.append("}");
-            }
-        } break;
-
-        case cppbor::TSTR:
-            out.append("'");
-            {
-                // TODO: escape "'" characters
-                out.append(item->asTstr()->value().c_str());
-            }
-            out.append("'");
-            break;
-
-        case cppbor::ARRAY: {
-            const cppbor::Array* array = item->asArray();
-            if (array->size() == 0) {
-                out.append("[]");
-            } else if (cborAreAllElementsNonCompound(array)) {
-                out.append("[");
-                for (size_t n = 0; n < array->size(); n++) {
-                    if (!cborPrettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
-                                                 mapKeysToNotPrint)) {
-                        return false;
-                    }
-                    out.append(", ");
-                }
-                out.append("]");
-            } else {
-                out.append("[\n" + indentString);
-                for (size_t n = 0; n < array->size(); n++) {
-                    out.append("  ");
-                    if (!cborPrettyPrintInternal((*array)[n].get(), out, indent + 2, maxBStrSize,
-                                                 mapKeysToNotPrint)) {
-                        return false;
-                    }
-                    out.append(",\n" + indentString);
-                }
-                out.append("]");
-            }
-        } break;
-
-        case cppbor::MAP: {
-            const cppbor::Map* map = item->asMap();
-
-            if (map->size() == 0) {
-                out.append("{}");
-            } else {
-                out.append("{\n" + indentString);
-                for (size_t n = 0; n < map->size(); n++) {
-                    out.append("  ");
-
-                    auto [map_key, map_value] = (*map)[n];
-
-                    if (!cborPrettyPrintInternal(map_key.get(), out, indent + 2, maxBStrSize,
-                                                 mapKeysToNotPrint)) {
-                        return false;
-                    }
-                    out.append(" : ");
-                    if (map_key->type() == cppbor::TSTR &&
-                        std::find(mapKeysToNotPrint.begin(), mapKeysToNotPrint.end(),
-                                  map_key->asTstr()->value()) != mapKeysToNotPrint.end()) {
-                        out.append("<not printed>");
-                    } else {
-                        if (!cborPrettyPrintInternal(map_value.get(), out, indent + 2, maxBStrSize,
-                                                     mapKeysToNotPrint)) {
-                            return false;
-                        }
-                    }
-                    out.append(",\n" + indentString);
-                }
-                out.append("}");
-            }
-        } break;
-
-        case cppbor::SEMANTIC: {
-            const cppbor::Semantic* semantic = item->asSemantic();
-            snprintf(buf, sizeof(buf), "tag %" PRIu64 " ", semantic->value());
-            out.append(buf);
-            cborPrettyPrintInternal(semantic->child().get(), out, indent, maxBStrSize,
-                                    mapKeysToNotPrint);
-        } break;
-
-        case cppbor::SIMPLE:
-            const cppbor::Bool* asBool = item->asSimple()->asBool();
-            const cppbor::Null* asNull = item->asSimple()->asNull();
-            if (asBool != nullptr) {
-                out.append(asBool->value() ? "true" : "false");
-            } else if (asNull != nullptr) {
-                out.append("null");
-            } else {
-                LOG(ERROR) << "Only boolean/null is implemented for SIMPLE";
-                return false;
-            }
-            break;
-    }
-
-    return true;
-}
-
-string cborPrettyPrint(const vector<uint8_t>& encodedCbor, size_t maxBStrSize,
-                       const vector<string>& mapKeysToNotPrint) {
-    auto [item, _, message] = cppbor::parse(encodedCbor);
-    if (item == nullptr) {
-        LOG(ERROR) << "Data to pretty print is not valid CBOR: " << message;
-        return "";
-    }
-
-    string out;
-    cborPrettyPrintInternal(item.get(), out, 0, maxBStrSize, mapKeysToNotPrint);
-    return out;
-}
-
-// ---------------------------------------------------------------------------
 // Crypto functionality / abstraction.
 // ---------------------------------------------------------------------------
 
-struct EVP_CIPHER_CTX_Deleter {
-    void operator()(EVP_CIPHER_CTX* ctx) const {
-        if (ctx != nullptr) {
-            EVP_CIPHER_CTX_free(ctx);
-        }
-    }
-};
-
-using EvpCipherCtxPtr = unique_ptr<EVP_CIPHER_CTX, EVP_CIPHER_CTX_Deleter>;
+using EvpCipherCtxPtr = bssl::UniquePtr<EVP_CIPHER_CTX>;
+using EC_KEY_Ptr = bssl::UniquePtr<EC_KEY>;
+using EVP_PKEY_Ptr = bssl::UniquePtr<EVP_PKEY>;
+using EVP_PKEY_CTX_Ptr = bssl::UniquePtr<EVP_PKEY_CTX>;
+using EC_GROUP_Ptr = bssl::UniquePtr<EC_GROUP>;
+using EC_POINT_Ptr = bssl::UniquePtr<EC_POINT>;
+using ECDSA_SIG_Ptr = bssl::UniquePtr<ECDSA_SIG>;
+using X509_Ptr = bssl::UniquePtr<X509>;
+using PKCS12_Ptr = bssl::UniquePtr<PKCS12>;
+using BIGNUM_Ptr = bssl::UniquePtr<BIGNUM>;
+using ASN1_INTEGER_Ptr = bssl::UniquePtr<ASN1_INTEGER>;
+using ASN1_TIME_Ptr = bssl::UniquePtr<ASN1_TIME>;
+using ASN1_OCTET_STRING_Ptr = bssl::UniquePtr<ASN1_OCTET_STRING>;
+using ASN1_OBJECT_Ptr = bssl::UniquePtr<ASN1_OBJECT>;
+using X509_NAME_Ptr = bssl::UniquePtr<X509_NAME>;
+using X509_EXTENSION_Ptr = bssl::UniquePtr<X509_EXTENSION>;
 
 // bool getRandom(size_t numBytes, vector<uint8_t>& output) {
 optional<vector<uint8_t>> getRandom(size_t numBytes) {
@@ -533,115 +348,6 @@
     return encryptedData;
 }
 
-struct EC_KEY_Deleter {
-    void operator()(EC_KEY* key) const {
-        if (key != nullptr) {
-            EC_KEY_free(key);
-        }
-    }
-};
-using EC_KEY_Ptr = unique_ptr<EC_KEY, EC_KEY_Deleter>;
-
-struct EVP_PKEY_Deleter {
-    void operator()(EVP_PKEY* key) const {
-        if (key != nullptr) {
-            EVP_PKEY_free(key);
-        }
-    }
-};
-using EVP_PKEY_Ptr = unique_ptr<EVP_PKEY, EVP_PKEY_Deleter>;
-
-struct EVP_PKEY_CTX_Deleter {
-    void operator()(EVP_PKEY_CTX* ctx) const {
-        if (ctx != nullptr) {
-            EVP_PKEY_CTX_free(ctx);
-        }
-    }
-};
-using EVP_PKEY_CTX_Ptr = unique_ptr<EVP_PKEY_CTX, EVP_PKEY_CTX_Deleter>;
-
-struct EC_GROUP_Deleter {
-    void operator()(EC_GROUP* group) const {
-        if (group != nullptr) {
-            EC_GROUP_free(group);
-        }
-    }
-};
-using EC_GROUP_Ptr = unique_ptr<EC_GROUP, EC_GROUP_Deleter>;
-
-struct EC_POINT_Deleter {
-    void operator()(EC_POINT* point) const {
-        if (point != nullptr) {
-            EC_POINT_free(point);
-        }
-    }
-};
-
-using EC_POINT_Ptr = unique_ptr<EC_POINT, EC_POINT_Deleter>;
-
-struct ECDSA_SIG_Deleter {
-    void operator()(ECDSA_SIG* sig) const {
-        if (sig != nullptr) {
-            ECDSA_SIG_free(sig);
-        }
-    }
-};
-using ECDSA_SIG_Ptr = unique_ptr<ECDSA_SIG, ECDSA_SIG_Deleter>;
-
-struct X509_Deleter {
-    void operator()(X509* x509) const {
-        if (x509 != nullptr) {
-            X509_free(x509);
-        }
-    }
-};
-using X509_Ptr = unique_ptr<X509, X509_Deleter>;
-
-struct PKCS12_Deleter {
-    void operator()(PKCS12* pkcs12) const {
-        if (pkcs12 != nullptr) {
-            PKCS12_free(pkcs12);
-        }
-    }
-};
-using PKCS12_Ptr = unique_ptr<PKCS12, PKCS12_Deleter>;
-
-struct BIGNUM_Deleter {
-    void operator()(BIGNUM* bignum) const {
-        if (bignum != nullptr) {
-            BN_free(bignum);
-        }
-    }
-};
-using BIGNUM_Ptr = unique_ptr<BIGNUM, BIGNUM_Deleter>;
-
-struct ASN1_INTEGER_Deleter {
-    void operator()(ASN1_INTEGER* value) const {
-        if (value != nullptr) {
-            ASN1_INTEGER_free(value);
-        }
-    }
-};
-using ASN1_INTEGER_Ptr = unique_ptr<ASN1_INTEGER, ASN1_INTEGER_Deleter>;
-
-struct ASN1_TIME_Deleter {
-    void operator()(ASN1_TIME* value) const {
-        if (value != nullptr) {
-            ASN1_TIME_free(value);
-        }
-    }
-};
-using ASN1_TIME_Ptr = unique_ptr<ASN1_TIME, ASN1_TIME_Deleter>;
-
-struct X509_NAME_Deleter {
-    void operator()(X509_NAME* value) const {
-        if (value != nullptr) {
-            X509_NAME_free(value);
-        }
-    }
-};
-using X509_NAME_Ptr = unique_ptr<X509_NAME, X509_NAME_Deleter>;
-
 vector<uint8_t> certificateChainJoin(const vector<vector<uint8_t>>& certificateChain) {
     vector<uint8_t> ret;
     for (const vector<uint8_t>& certificate : certificateChain) {
@@ -934,18 +640,26 @@
 optional<vector<vector<uint8_t>>> createAttestation(
         const EVP_PKEY* key, const vector<uint8_t>& applicationId, const vector<uint8_t>& challenge,
         uint64_t activeTimeMilliSeconds, uint64_t expireTimeMilliSeconds, bool isTestCredential) {
-    const keymaster_cert_chain_t* attestation_chain =
-            ::keymaster::getAttestationChain(KM_ALGORITHM_EC, nullptr);
-    if (attestation_chain == nullptr) {
-        LOG(ERROR) << "Error getting attestation chain";
+    // Pretend to be implemented in a trusted environment just so we can pass
+    // the VTS tests. Of course, this is a pretend-only game since hopefully no
+    // relying party is ever going to trust our batch key and those keys above
+    // it.
+    ::keymaster::PureSoftKeymasterContext context(::keymaster::KmVersion::KEYMINT_1,
+                                                  KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
+
+    keymaster_error_t error;
+    ::keymaster::CertificateChain attestation_chain =
+            context.GetAttestationChain(KM_ALGORITHM_EC, &error);
+    if (KM_ERROR_OK != error) {
+        LOG(ERROR) << "Error getting attestation chain " << error;
         return {};
     }
     if (expireTimeMilliSeconds == 0) {
-        if (attestation_chain->entry_count < 1) {
+        if (attestation_chain.entry_count < 1) {
             LOG(ERROR) << "Expected at least one entry in attestation chain";
             return {};
         }
-        keymaster_blob_t* bcBlob = &(attestation_chain->entries[0]);
+        keymaster_blob_t* bcBlob = &(attestation_chain.entries[0]);
         const uint8_t* bcData = bcBlob->data;
         auto bc = X509_Ptr(d2i_X509(nullptr, &bcData, bcBlob->data_length));
         time_t bcNotAfter;
@@ -955,15 +669,23 @@
         }
         expireTimeMilliSeconds = bcNotAfter * 1000;
     }
-    const keymaster_key_blob_t* attestation_signing_key =
-            ::keymaster::getAttestationKey(KM_ALGORITHM_EC, nullptr);
-    if (attestation_signing_key == nullptr) {
-        LOG(ERROR) << "Error getting attestation key";
+
+    ::keymaster::X509_NAME_Ptr subjectName;
+    if (KM_ERROR_OK !=
+        ::keymaster::make_name_from_str("Android Identity Credential Key", &subjectName)) {
+        LOG(ERROR) << "Cannot create attestation subject";
         return {};
     }
 
+    vector<uint8_t> subject(i2d_X509_NAME(subjectName.get(), NULL));
+    unsigned char* subjectPtr = subject.data();
+
+    i2d_X509_NAME(subjectName.get(), &subjectPtr);
+
     ::keymaster::AuthorizationSet auth_set(
             ::keymaster::AuthorizationSetBuilder()
+                    .Authorization(::keymaster::TAG_CERTIFICATE_NOT_BEFORE, activeTimeMilliSeconds)
+                    .Authorization(::keymaster::TAG_CERTIFICATE_NOT_AFTER, expireTimeMilliSeconds)
                     .Authorization(::keymaster::TAG_ATTESTATION_CHALLENGE, challenge.data(),
                                    challenge.size())
                     .Authorization(::keymaster::TAG_ACTIVE_DATETIME, activeTimeMilliSeconds)
@@ -976,6 +698,8 @@
                     // includes app id.
                     .Authorization(::keymaster::TAG_ATTESTATION_APPLICATION_ID,
                                    applicationId.data(), applicationId.size())
+                    .Authorization(::keymaster::TAG_CERTIFICATE_SUBJECT, subject.data(),
+                                   subject.size())
                     .Authorization(::keymaster::TAG_USAGE_EXPIRE_DATETIME, expireTimeMilliSeconds));
 
     // Unique id and device id is not applicable for identity credential attestation,
@@ -1000,34 +724,22 @@
     }
     ::keymaster::AuthorizationSet hwEnforced(hwEnforcedBuilder);
 
-    keymaster_error_t error;
-    ::keymaster::CertChainPtr cert_chain_out;
+    ::keymaster::CertificateChain cert_chain_out = generate_attestation(
+            key, swEnforced, hwEnforced, auth_set, {} /* attest_key */, context, &error);
 
-    // Pretend to be implemented in a trusted environment just so we can pass
-    // the VTS tests. Of course, this is a pretend-only game since hopefully no
-    // relying party is ever going to trust our batch key and those keys above
-    // it.
-    //
-    ::keymaster::PureSoftKeymasterContext context(KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT);
-
-    error = generate_attestation_from_EVP(key, swEnforced, hwEnforced, auth_set, context,
-                                          ::keymaster::kCurrentKeymasterVersion, *attestation_chain,
-                                          *attestation_signing_key,
-                                          "Android Identity Credential Key", &cert_chain_out);
-
-    if (KM_ERROR_OK != error || !cert_chain_out) {
-        LOG(ERROR) << "Error generate attestation from EVP key" << error;
+    if (KM_ERROR_OK != error) {
+        LOG(ERROR) << "Error generating attestation from EVP key: " << error;
         return {};
     }
 
-    // translate certificate format from keymaster_cert_chain_t to vector<uint8_t>.
+    // translate certificate format from keymaster_cert_chain_t to vector<vector<uint8_t>>.
     vector<vector<uint8_t>> attestationCertificate;
-    for (int i = 0; i < cert_chain_out->entry_count; i++) {
+    for (std::size_t i = 0; i < cert_chain_out.entry_count; i++) {
         attestationCertificate.insert(
                 attestationCertificate.end(),
                 vector<uint8_t>(
-                        cert_chain_out->entries[i].data,
-                        cert_chain_out->entries[i].data + cert_chain_out->entries[i].data_length));
+                        cert_chain_out.entries[i].data,
+                        cert_chain_out.entries[i].data + cert_chain_out.entries[i].data_length));
     }
 
     return attestationCertificate;
@@ -1209,8 +921,19 @@
         return {};
     }
     vector<uint8_t> privateKey;
-    privateKey.resize(BN_num_bytes(bignum));
-    BN_bn2bin(bignum, privateKey.data());
+
+    // Note that this may return fewer than 32 bytes so pad with zeroes since we
+    // want to always return 32 bytes.
+    size_t numBytes = BN_num_bytes(bignum);
+    if (numBytes > 32) {
+        LOG(ERROR) << "Size is " << numBytes << ", expected this to be 32 or less";
+        return {};
+    }
+    privateKey.resize(32);
+    for (size_t n = 0; n < 32 - numBytes; n++) {
+        privateKey[n] = 0x00;
+    }
+    BN_bn2bin(bignum, privateKey.data() + 32 - numBytes);
     return privateKey;
 }
 
@@ -1367,7 +1090,8 @@
 optional<vector<uint8_t>> ecPublicKeyGenerateCertificate(
         const vector<uint8_t>& publicKey, const vector<uint8_t>& signingKey,
         const string& serialDecimal, const string& issuer, const string& subject,
-        time_t validityNotBefore, time_t validityNotAfter) {
+        time_t validityNotBefore, time_t validityNotAfter,
+        const map<string, vector<uint8_t>>& extensions) {
     auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
     auto point = EC_POINT_Ptr(EC_POINT_new(group.get()));
     if (EC_POINT_oct2point(group.get(), point.get(), publicKey.data(), publicKey.size(), nullptr) !=
@@ -1470,6 +1194,32 @@
         return {};
     }
 
+    for (auto const& [oidStr, blob] : extensions) {
+        ASN1_OBJECT_Ptr oid(
+                OBJ_txt2obj(oidStr.c_str(), 1));  // accept numerical dotted string form only
+        if (!oid.get()) {
+            LOG(ERROR) << "Error setting OID";
+            return {};
+        }
+        ASN1_OCTET_STRING_Ptr octetString(ASN1_OCTET_STRING_new());
+        if (!ASN1_OCTET_STRING_set(octetString.get(), blob.data(), blob.size())) {
+            LOG(ERROR) << "Error setting octet string for extension";
+            return {};
+        }
+
+        X509_EXTENSION_Ptr extension = X509_EXTENSION_Ptr(X509_EXTENSION_new());
+        extension.reset(X509_EXTENSION_create_by_OBJ(nullptr, oid.get(), 0 /* not critical */,
+                                                     octetString.get()));
+        if (!extension.get()) {
+            LOG(ERROR) << "Error setting extension";
+            return {};
+        }
+        if (!X509_add_ext(x509.get(), extension.get(), -1)) {
+            LOG(ERROR) << "Error adding extension";
+            return {};
+        }
+    }
+
     if (X509_sign(x509.get(), privPkey.get(), EVP_sha256()) == 0) {
         LOG(ERROR) << "Error signing X509 certificate";
         return {};
@@ -1611,12 +1361,6 @@
         return {};
     }
 
-    int algoId = OBJ_obj2nid(certs[0]->cert_info->key->algor->algorithm);
-    if (algoId != NID_X9_62_id_ecPublicKey) {
-        LOG(ERROR) << "Expected NID_X9_62_id_ecPublicKey, got " << OBJ_nid2ln(algoId);
-        return {};
-    }
-
     auto pkey = EVP_PKEY_Ptr(X509_get_pubkey(certs[0].get()));
     if (pkey.get() == nullptr) {
         LOG(ERROR) << "No public key";
@@ -1644,6 +1388,44 @@
     return publicKey;
 }
 
+optional<vector<uint8_t>> certificateGetExtension(const vector<uint8_t>& x509Certificate,
+                                                  const string& oidStr) {
+    vector<X509_Ptr> certs;
+    if (!parseX509Certificates(x509Certificate, certs)) {
+        return {};
+    }
+    if (certs.size() < 1) {
+        LOG(ERROR) << "No certificates in chain";
+        return {};
+    }
+
+    ASN1_OBJECT_Ptr oid(
+            OBJ_txt2obj(oidStr.c_str(), 1));  // accept numerical dotted string form only
+    if (!oid.get()) {
+        LOG(ERROR) << "Error setting OID";
+        return {};
+    }
+
+    int location = X509_get_ext_by_OBJ(certs[0].get(), oid.get(), -1 /* search from beginning */);
+    if (location == -1) {
+        return {};
+    }
+
+    X509_EXTENSION* ext = X509_get_ext(certs[0].get(), location);
+    if (ext == nullptr) {
+        return {};
+    }
+
+    ASN1_OCTET_STRING* octetString = X509_EXTENSION_get_data(ext);
+    if (octetString == nullptr) {
+        return {};
+    }
+    vector<uint8_t> result;
+    result.resize(octetString->length);
+    memcpy(result.data(), octetString->data, octetString->length);
+    return result;
+}
+
 optional<pair<size_t, size_t>> certificateFindPublicKey(const vector<uint8_t>& x509Certificate) {
     vector<X509_Ptr> certs;
     if (!parseX509Certificates(x509Certificate, certs)) {
@@ -1870,11 +1652,11 @@
 
     ecdsaCoseSignature.clear();
     ecdsaCoseSignature.resize(64);
-    if (BN_bn2binpad(sig->r, ecdsaCoseSignature.data(), 32) != 32) {
+    if (BN_bn2binpad(ECDSA_SIG_get0_r(sig), ecdsaCoseSignature.data(), 32) != 32) {
         LOG(ERROR) << "Error encoding r";
         return false;
     }
-    if (BN_bn2binpad(sig->s, ecdsaCoseSignature.data() + 32, 32) != 32) {
+    if (BN_bn2binpad(ECDSA_SIG_get0_s(sig), ecdsaCoseSignature.data() + 32, 32) != 32) {
         LOG(ERROR) << "Error encoding s";
         return false;
     }
@@ -2164,7 +1946,7 @@
     }
 
     for (size_t n = 0; n < protectedHeaders->size(); n++) {
-        auto [keyItem, valueItem] = (*protectedHeaders)[n];
+        auto& [keyItem, valueItem] = (*protectedHeaders)[n];
         const cppbor::Int* number = keyItem->asInt();
         if (number == nullptr) {
             LOG(ERROR) << "Key item in top-level map is not a number";
@@ -2207,7 +1989,7 @@
     }
 
     for (size_t n = 0; n < unprotectedHeaders->size(); n++) {
-        auto [keyItem, valueItem] = (*unprotectedHeaders)[n];
+        auto& [keyItem, valueItem] = (*unprotectedHeaders)[n];
         const cppbor::Int* number = keyItem->asInt();
         if (number == nullptr) {
             LOG(ERROR) << "Key item in top-level map is not a number";
@@ -2359,9 +2141,9 @@
                     .add("DeviceAuthentication")
                     .add(std::move(sessionTranscriptItem))
                     .add(docType)
-                    .add(cppbor::Semantic(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
+                    .add(cppbor::SemanticTag(kSemanticTagEncodedCbor, deviceNameSpacesEncoded));
     vector<uint8_t> deviceAuthenticationBytes =
-            cppbor::Semantic(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
+            cppbor::SemanticTag(kSemanticTagEncodedCbor, deviceAuthentication.encode()).encode();
     optional<vector<uint8_t>> calculatedMac =
             support::coseMac0(eMacKey, {},                 // payload
                               deviceAuthenticationBytes);  // detached content
@@ -2394,7 +2176,6 @@
     return ret;
 }
 
-
 vector<uint8_t> testHardwareBoundKey = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 
 const vector<uint8_t>& getTestHardwareBoundKey() {
diff --git a/identity/support/tests/IdentityCredentialSupportTest.cpp b/identity/support/tests/IdentityCredentialSupportTest.cpp
index 266f263..4c9b87a 100644
--- a/identity/support/tests/IdentityCredentialSupportTest.cpp
+++ b/identity/support/tests/IdentityCredentialSupportTest.cpp
@@ -55,99 +55,6 @@
     EXPECT_FALSE(support::decodeHex("012"));
 }
 
-TEST(IdentityCredentialSupport, CborPrettyPrint) {
-    EXPECT_EQ("'Some text'", support::cborPrettyPrint(cppbor::Tstr("Some text").encode()));
-
-    EXPECT_EQ("''", support::cborPrettyPrint(cppbor::Tstr("").encode()));
-
-    EXPECT_EQ("{0x01, 0x00, 0x02, 0xf0, 0xff, 0x40}",
-              support::cborPrettyPrint(
-                      cppbor::Bstr(vector<uint8_t>({1, 0, 2, 240, 255, 64})).encode()));
-
-    EXPECT_EQ("{}", support::cborPrettyPrint(cppbor::Bstr(vector<uint8_t>()).encode()));
-
-    EXPECT_EQ("true", support::cborPrettyPrint(cppbor::Bool(true).encode()));
-
-    EXPECT_EQ("false", support::cborPrettyPrint(cppbor::Bool(false).encode()));
-
-    EXPECT_EQ("42", support::cborPrettyPrint(cppbor::Uint(42).encode()));
-
-    EXPECT_EQ("9223372036854775807",  // 0x7fff ffff ffff ffff
-              support::cborPrettyPrint(cppbor::Uint(std::numeric_limits<int64_t>::max()).encode()));
-
-    EXPECT_EQ("-42", support::cborPrettyPrint(cppbor::Nint(-42).encode()));
-
-    EXPECT_EQ("-9223372036854775808",  // -0x8000 0000 0000 0000
-              support::cborPrettyPrint(cppbor::Nint(std::numeric_limits<int64_t>::min()).encode()));
-}
-
-TEST(IdentityCredentialSupport, CborPrettyPrintCompound) {
-    cppbor::Array array = cppbor::Array("foo", "bar", "baz");
-    EXPECT_EQ("['foo', 'bar', 'baz', ]", support::cborPrettyPrint(array.encode()));
-
-    cppbor::Map map = cppbor::Map().add("foo", 42).add("bar", 43).add("baz", 44);
-    EXPECT_EQ(
-            "{\n"
-            "  'foo' : 42,\n"
-            "  'bar' : 43,\n"
-            "  'baz' : 44,\n"
-            "}",
-            support::cborPrettyPrint(map.encode()));
-
-    cppbor::Array array2 = cppbor::Array(cppbor::Tstr("Some text"), cppbor::Nint(-42));
-    EXPECT_EQ("['Some text', -42, ]", support::cborPrettyPrint(array2.encode()));
-
-    cppbor::Map map2 = cppbor::Map().add(42, "foo").add(43, "bar").add(44, "baz");
-    EXPECT_EQ(
-            "{\n"
-            "  42 : 'foo',\n"
-            "  43 : 'bar',\n"
-            "  44 : 'baz',\n"
-            "}",
-            support::cborPrettyPrint(map2.encode()));
-
-    cppbor::Array deeplyNestedArrays =
-            cppbor::Array(cppbor::Array(cppbor::Array("a", "b", "c")),
-                          cppbor::Array(cppbor::Array("d", "e", cppbor::Array("f", "g"))));
-    EXPECT_EQ(
-            "[\n"
-            "  ['a', 'b', 'c', ],\n"
-            "  [\n    'd',\n"
-            "    'e',\n"
-            "    ['f', 'g', ],\n"
-            "  ],\n"
-            "]",
-            support::cborPrettyPrint(deeplyNestedArrays.encode()));
-
-    EXPECT_EQ(
-            "[\n"
-            "  {0x0a, 0x0b},\n"
-            "  'foo',\n"
-            "  42,\n"
-            "  ['foo', 'bar', 'baz', ],\n"
-            "  {\n"
-            "    'foo' : 42,\n"
-            "    'bar' : 43,\n"
-            "    'baz' : 44,\n"
-            "  },\n"
-            "  {\n"
-            "    'deep1' : ['Some text', -42, ],\n"
-            "    'deep2' : {\n"
-            "      42 : 'foo',\n"
-            "      43 : 'bar',\n"
-            "      44 : 'baz',\n"
-            "    },\n"
-            "  },\n"
-            "]",
-            support::cborPrettyPrint(cppbor::Array(cppbor::Bstr(vector<uint8_t>{10, 11}),
-                                                   cppbor::Tstr("foo"), cppbor::Uint(42),
-                                                   std::move(array), std::move(map),
-                                                   (cppbor::Map()
-                                                            .add("deep1", std::move(array2))
-                                                            .add("deep2", std::move(map2))))
-                                             .encode()));
-}
-
 TEST(IdentityCredentialSupport, Signatures) {
     vector<uint8_t> data = {1, 2, 3};
 
@@ -219,7 +126,7 @@
     ASSERT_EQ(data, payload.value());
 
     // Finally, check that |coseSign1| are the bytes of a valid COSE_Sign1 message
-    string out = support::cborPrettyPrint(coseSign1.value());
+    string out = cppbor::prettyPrint(coseSign1.value());
     out = replaceLine(out, -2, "  [] // Signature Removed");
     EXPECT_EQ(
             "[\n"
@@ -250,7 +157,7 @@
     ASSERT_EQ(0, payload.value().size());
 
     // Finally, check that |coseSign1| are the bytes of a valid COSE_Sign1 message
-    string out = support::cborPrettyPrint(coseSign1.value());
+    string out = cppbor::prettyPrint(coseSign1.value());
     out = replaceLine(out, -2, "  [] // Signature Removed");
     EXPECT_EQ(
             "[\n"
@@ -271,7 +178,7 @@
         optional<vector<uint8_t>> pubKey = support::ecKeyPairGetPublicKey(keyPair.value());
 
         optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
-                pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0);
+                pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {});
         certs.push_back(cert.value());
     }
     return support::certificateChainJoin(certs);
@@ -338,7 +245,7 @@
     ASSERT_TRUE(pubKey);
 
     optional<vector<uint8_t>> cert = support::ecPublicKeyGenerateCertificate(
-            pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0);
+            pubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {});
 
     optional<vector<uint8_t>> extractedPubKey =
             support::certificateChainGetTopMostKey(cert.value());
@@ -358,7 +265,7 @@
     optional<vector<uint8_t>> otherPubKey = support::ecKeyPairGetPublicKey(keyPair.value());
     ASSERT_TRUE(otherPubKey);
     optional<vector<uint8_t>> otherCert = support::ecPublicKeyGenerateCertificate(
-            otherPubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0);
+            otherPubKey.value(), privKey.value(), "0001", "someIssuer", "someSubject", 0, 0, {});
 
     // Now both cert and otherCert are two distinct certificates. Let's make a
     // chain and check that certificateChainSplit() works as expected.
@@ -411,7 +318,7 @@
             "0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
             "0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
             "]",
-            support::cborPrettyPrint(mac.value()));
+            cppbor::prettyPrint(mac.value()));
 }
 
 TEST(IdentityCredentialSupport, CoseMac0DetachedContent) {
@@ -433,7 +340,7 @@
             "0x86, 0x5c, 0x28, 0x2c, 0xd5, 0xa5, 0x13, 0xff, 0x3b, 0xd1, 0xde, 0x70, 0x5e, 0xbb, "
             "0xe2, 0x2d, 0x42, 0xbe, 0x53},\n"
             "]",
-            support::cborPrettyPrint(mac.value()));
+            cppbor::prettyPrint(mac.value()));
 }
 
 // Generates a private key in DER format for a small value of 'd'.
@@ -460,8 +367,8 @@
 
 const cppbor::Item* findValueForTstr(const cppbor::Map* map, const string& keyValue) {
     // TODO: Need cast until libcppbor's Map::get() is marked as const
-    auto [item, found] = ((cppbor::Map*)map)->get(keyValue);
-    if (!found) {
+    const auto& item = map->get(keyValue);
+    if (!item) {
         return nullptr;
     }
     return item.get();
@@ -483,12 +390,13 @@
     return item->asMap();
 }
 
-const cppbor::Semantic* findSemanticValueForTstr(const cppbor::Map* map, const string& keyValue) {
+const cppbor::SemanticTag* findSemanticValueForTstr(const cppbor::Map* map,
+                                                    const string& keyValue) {
     const cppbor::Item* item = findValueForTstr(map, keyValue);
     if (item == nullptr) {
         return nullptr;
     }
-    return item->asSemantic();
+    return item->asSemanticTag();
 }
 
 const std::string findStringValueForTstr(const cppbor::Map* map, const string& keyValue) {
@@ -576,11 +484,11 @@
     auto [sessionEstablishmentItem, _se, _se2] = cppbor::parse(sessionEstablishmentEncoded.value());
     const cppbor::Map* sessionEstablishment = sessionEstablishmentItem->asMap();
     ASSERT_NE(sessionEstablishment, nullptr);
-    const cppbor::Semantic* eReaderKeyBytes =
+    const cppbor::SemanticTag* eReaderKeyBytes =
             findSemanticValueForTstr(sessionEstablishment, "eReaderKeyBytes");
     ASSERT_NE(eReaderKeyBytes, nullptr);
-    ASSERT_EQ(eReaderKeyBytes->value(), 24);
-    const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->child()->asBstr();
+    ASSERT_EQ(eReaderKeyBytes->semanticTag(), 24);
+    const cppbor::Bstr* eReaderKeyBstr = eReaderKeyBytes->asBstr();
     ASSERT_NE(eReaderKeyBstr, nullptr);
     vector<uint8_t> eReaderKeyEncoded = eReaderKeyBstr->value();
     // TODO: verify this agrees with ephemeralReaderKeyX and ephemeralReaderKeyY
@@ -605,12 +513,12 @@
     //   SessionTranscriptBytes = #6.24(bstr .cbor SessionTranscript)
     //
     cppbor::Array sessionTranscript;
-    sessionTranscript.add(cppbor::Semantic(24, deviceEngagementEncoded));
-    sessionTranscript.add(cppbor::Semantic(24, eReaderKeyEncoded));
+    sessionTranscript.add(cppbor::SemanticTag(24, deviceEngagementEncoded));
+    sessionTranscript.add(cppbor::SemanticTag(24, eReaderKeyEncoded));
     sessionTranscript.add(cppbor::Null());
     vector<uint8_t> sessionTranscriptEncoded = sessionTranscript.encode();
     vector<uint8_t> sessionTranscriptBytes =
-            cppbor::Semantic(24, sessionTranscriptEncoded).encode();
+            cppbor::SemanticTag(24, sessionTranscriptEncoded).encode();
 
     // The expected EMacKey is 4c1ebb8aacc633465390fa44edfdb49cb57f2e079aaa771d812584699c0b97e2
     //
@@ -696,11 +604,11 @@
 
     // Dig out the encoded form of DeviceNameSpaces
     //
-    const cppbor::Semantic* deviceNameSpacesBytes =
+    const cppbor::SemanticTag* deviceNameSpacesBytes =
             findSemanticValueForTstr(deviceSigned, "nameSpaces");
     ASSERT_NE(deviceNameSpacesBytes, nullptr);
-    ASSERT_EQ(deviceNameSpacesBytes->value(), 24);
-    const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->child()->asBstr();
+    ASSERT_EQ(deviceNameSpacesBytes->semanticTag(), 24);
+    const cppbor::Bstr* deviceNameSpacesBstr = deviceNameSpacesBytes->asBstr();
     ASSERT_NE(deviceNameSpacesBstr, nullptr);
     vector<uint8_t> deviceNameSpacesEncoded = deviceNameSpacesBstr->value();
 
diff --git a/input/classifier/1.0/Android.bp b/input/classifier/1.0/Android.bp
index 11e0f52..37e522d 100644
--- a/input/classifier/1.0/Android.bp
+++ b/input/classifier/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.input.classifier@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IInputClassifier.hal",
     ],
diff --git a/input/classifier/1.0/default/Android.bp b/input/classifier/1.0/default/Android.bp
index 3379a76..8ab2ba8 100644
--- a/input/classifier/1.0/default/Android.bp
+++ b/input/classifier/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.input.classifier@1.0-service.default",
     init_rc: ["android.hardware.input.classifier@1.0-service.default.rc"],
diff --git a/input/classifier/1.0/vts/functional/Android.bp b/input/classifier/1.0/vts/functional/Android.bp
index 4d6c9c3..5964a7e 100644
--- a/input/classifier/1.0/vts/functional/Android.bp
+++ b/input/classifier/1.0/vts/functional/Android.bp
@@ -14,10 +14,20 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalInputClassifierV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
     srcs: ["VtsHalInputClassifierV1_0TargetTest.cpp"],
+    header_libs: ["jni_headers"],
     static_libs: [
         "android.hardware.input.classifier@1.0",
         "android.hardware.input.common@1.0",
diff --git a/input/common/1.0/Android.bp b/input/common/1.0/Android.bp
index 2c7c517..ed0ab98 100644
--- a/input/common/1.0/Android.bp
+++ b/input/common/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.input.common@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
     ],
diff --git a/ir/1.0/Android.bp b/ir/1.0/Android.bp
index 5fca96d..7ed7429 100644
--- a/ir/1.0/Android.bp
+++ b/ir/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.ir@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IConsumerIr.hal",
diff --git a/ir/1.0/default/Android.bp b/ir/1.0/default/Android.bp
index 80e0f3c..9b99f13 100644
--- a/ir/1.0/default/Android.bp
+++ b/ir/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // 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.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.ir@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/ir/1.0/vts/functional/Android.bp b/ir/1.0/vts/functional/Android.bp
index 160129f..983f2a4 100644
--- a/ir/1.0/vts/functional/Android.bp
+++ b/ir/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalIrV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp b/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
index a5dbdcc..81464f0 100644
--- a/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
+++ b/ir/1.0/vts/functional/VtsHalIrV1_0TargetTest.cpp
@@ -77,6 +77,7 @@
   EXPECT_FALSE(ir->transmit(-1, vec));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ConsumerIrHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ConsumerIrHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IConsumerIr::descriptor)),
diff --git a/keymaster/3.0/Android.bp b/keymaster/3.0/Android.bp
index 0fdc32c..12f6e3c 100644
--- a/keymaster/3.0/Android.bp
+++ b/keymaster/3.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.keymaster@3.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IKeymasterDevice.hal",
diff --git a/keymaster/3.0/IKeymasterDevice.hal b/keymaster/3.0/IKeymasterDevice.hal
index 2664765..9bd8602 100644
--- a/keymaster/3.0/IKeymasterDevice.hal
+++ b/keymaster/3.0/IKeymasterDevice.hal
@@ -20,6 +20,7 @@
  * Keymaster device definition.  For thorough documentation see the implementer's reference, at
  * https://source.android.com/security/keystore/implementer-ref.html
  */
+@SensitiveData
 interface IKeymasterDevice {
 
     /**
diff --git a/keymaster/3.0/default/Android.mk b/keymaster/3.0/default/Android.mk
index 208cb66..053ad67 100644
--- a/keymaster/3.0/default/Android.mk
+++ b/keymaster/3.0/default/Android.mk
@@ -2,6 +2,9 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.keymaster@3.0-impl
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SRC_FILES := \
@@ -25,6 +28,9 @@
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_MODULE := android.hardware.keymaster@3.0-service
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_INIT_RC := android.hardware.keymaster@3.0-service.rc
 LOCAL_SRC_FILES := \
     service.cpp
diff --git a/keymaster/3.0/default/KeymasterDevice.cpp b/keymaster/3.0/default/KeymasterDevice.cpp
index 7d3e6f2..8b416c3 100644
--- a/keymaster/3.0/default/KeymasterDevice.cpp
+++ b/keymaster/3.0/default/KeymasterDevice.cpp
@@ -22,7 +22,6 @@
 #include <log/log.h>
 
 #include <AndroidKeymaster3Device.h>
-#include <hardware/keymaster0.h>
 #include <hardware/keymaster1.h>
 #include <hardware/keymaster2.h>
 #include <hardware/keymaster_defs.h>
@@ -33,16 +32,6 @@
 namespace V3_0 {
 namespace implementation {
 
-static int get_keymaster0_dev(keymaster0_device_t** dev, const hw_module_t* mod) {
-    int rc = keymaster0_open(mod, dev);
-    if (rc) {
-        ALOGE("Error opening keystore keymaster0 device.");
-        *dev = nullptr;
-        return rc;
-    }
-    return 0;
-}
-
 static int get_keymaster1_dev(keymaster1_device_t** dev, const hw_module_t* mod) {
     int rc = keymaster1_open(mod, dev);
     if (rc) {
@@ -75,11 +64,7 @@
     }
 
     if (mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0) {
-        keymaster0_device_t* dev = nullptr;
-        if (get_keymaster0_dev(&dev, mod)) {
-            return nullptr;
-        }
-        return ::keymaster::ng::CreateKeymasterDevice(dev);
+        return nullptr;
     } else if (mod->module_api_version == KEYMASTER_MODULE_API_VERSION_1_0) {
         keymaster1_device_t* dev = nullptr;
         if (get_keymaster1_dev(&dev, mod)) {
diff --git a/keymaster/3.0/vts/functional/Android.bp b/keymaster/3.0/vts/functional/Android.bp
index 35b9387..e2ae803 100644
--- a/keymaster/3.0/vts/functional/Android.bp
+++ b/keymaster/3.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalKeymasterV3_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp
index ae32764..554afe7 100644
--- a/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/3.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -4179,39 +4179,51 @@
 static const auto kKeymasterDeviceChoices =
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IKeymasterDevice::descriptor));
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NewKeyGenerationTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, NewKeyGenerationTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(KeymasterVersionTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, KeymasterVersionTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GetKeyCharacteristicsTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, GetKeyCharacteristicsTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SigningOperationsTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, SigningOperationsTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VerificationOperationsTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, VerificationOperationsTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ExportKeyTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, ExportKeyTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ImportKeyTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, ImportKeyTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(EncryptionOperationsTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, EncryptionOperationsTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MaxOperationsTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, MaxOperationsTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AddEntropyTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, AddEntropyTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AttestationTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, AttestationTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(KeyDeletionTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, KeyDeletionTest, kKeymasterDeviceChoices,
                          android::hardware::PrintInstanceNameToString);
 
diff --git a/keymaster/4.0/Android.bp b/keymaster/4.0/Android.bp
index ea328f4..8b4bc5b 100644
--- a/keymaster/4.0/Android.bp
+++ b/keymaster/4.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.keymaster@4.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IKeymasterDevice.hal",
diff --git a/keymaster/4.0/IKeymasterDevice.hal b/keymaster/4.0/IKeymasterDevice.hal
index 3475f79..dfde060 100644
--- a/keymaster/4.0/IKeymasterDevice.hal
+++ b/keymaster/4.0/IKeymasterDevice.hal
@@ -195,7 +195,7 @@
  * Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
  * IKeymasterDevice key, as described in the Key Access Control section above.
  */
-
+@SensitiveData
 interface IKeymasterDevice {
 
     /**
diff --git a/keymaster/4.0/default/Android.bp b/keymaster/4.0/default/Android.bp
index f9e3986..702a716 100644
--- a/keymaster/4.0/default/Android.bp
+++ b/keymaster/4.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.keymaster@4.0-service",
     defaults: ["hidl_defaults"],
diff --git a/keymaster/4.0/support/Android.bp b/keymaster/4.0/support/Android.bp
index 9c5fbab..efb64c3 100644
--- a/keymaster/4.0/support/Android.bp
+++ b/keymaster/4.0/support/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "libkeymaster4support",
     vendor_available: true,
diff --git a/keymaster/4.0/support/attestation_record.cpp b/keymaster/4.0/support/attestation_record.cpp
index bc294bd..342b3e2 100644
--- a/keymaster/4.0/support/attestation_record.cpp
+++ b/keymaster/4.0/support/attestation_record.cpp
@@ -71,6 +71,7 @@
     ASN1_INTEGER_SET* padding;
     ASN1_INTEGER* ec_curve;
     ASN1_INTEGER* rsa_public_exponent;
+    ASN1_NULL* rollback_resistance;
     ASN1_INTEGER* active_date_time;
     ASN1_INTEGER* origination_expire_date_time;
     ASN1_INTEGER* usage_expire_date_time;
@@ -78,56 +79,84 @@
     ASN1_INTEGER* user_auth_type;
     ASN1_INTEGER* auth_timeout;
     ASN1_NULL* allow_while_on_body;
+    ASN1_NULL* trusted_user_presence_required;
+    ASN1_NULL* trusted_confirmation_required;
+    ASN1_NULL* unlocked_device_required;
     ASN1_NULL* all_applications;
     ASN1_OCTET_STRING* application_id;
     ASN1_INTEGER* creation_date_time;
     ASN1_INTEGER* origin;
-    ASN1_NULL* rollback_resistance;
     KM_ROOT_OF_TRUST* root_of_trust;
     ASN1_INTEGER* os_version;
     ASN1_INTEGER* os_patchlevel;
     ASN1_OCTET_STRING* attestation_application_id;
-    ASN1_NULL* trusted_user_presence_required;
-    ASN1_NULL* trusted_confirmation_required;
-    ASN1_NULL* unlocked_device_required;
+    ASN1_OCTET_STRING* attestation_id_brand;
+    ASN1_OCTET_STRING* attestation_id_device;
+    ASN1_OCTET_STRING* attestation_id_product;
+    ASN1_OCTET_STRING* attestation_id_serial;
+    ASN1_OCTET_STRING* attestation_id_imei;
+    ASN1_OCTET_STRING* attestation_id_meid;
+    ASN1_OCTET_STRING* attestation_id_manufacturer;
+    ASN1_OCTET_STRING* attestation_id_model;
     ASN1_INTEGER* vendor_patchlevel;
     ASN1_INTEGER* boot_patchlevel;
 } KM_AUTH_LIST;
 
 ASN1_SEQUENCE(KM_AUTH_LIST) = {
-    ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
-    ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
-    ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
-                 TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL, TAG_ROLLBACK_RESISTANCE.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
-                 TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
-                 TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL, TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
-                 TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
-                 TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
-                 TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER, TAG_CREATION_DATETIME.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER, TAG_VENDOR_PATCHLEVEL.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
-    ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
-                 TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+                     TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
+                     TAG_ROLLBACK_RESISTANCE.maskedTag()),
+
+        ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+                     TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+                     TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+                     TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
+                     TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
+                     TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
+                     TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+                     TAG_CREATION_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_brand, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_BRAND.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_device, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_DEVICE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_product, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_PRODUCT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_serial, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_SERIAL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_imei, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_IMEI.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_meid, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MEID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_manufacturer, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MANUFACTURER.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_model, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MODEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
+                     TAG_VENDOR_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
 } ASN1_SEQUENCE_END(KM_AUTH_LIST);
 IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
 
@@ -259,6 +288,14 @@
     copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
     copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
     copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+    copyAuthTag(record->attestation_id_brand, TAG_ATTESTATION_ID_BRAND, auth_list);
+    copyAuthTag(record->attestation_id_device, TAG_ATTESTATION_ID_DEVICE, auth_list);
+    copyAuthTag(record->attestation_id_product, TAG_ATTESTATION_ID_PRODUCT, auth_list);
+    copyAuthTag(record->attestation_id_serial, TAG_ATTESTATION_ID_SERIAL, auth_list);
+    copyAuthTag(record->attestation_id_imei, TAG_ATTESTATION_ID_IMEI, auth_list);
+    copyAuthTag(record->attestation_id_meid, TAG_ATTESTATION_ID_MEID, auth_list);
+    copyAuthTag(record->attestation_id_manufacturer, TAG_ATTESTATION_ID_MANUFACTURER, auth_list);
+    copyAuthTag(record->attestation_id_model, TAG_ATTESTATION_ID_MODEL, auth_list);
     copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
     copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
     copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
diff --git a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
index bc7f311..ea40971 100644
--- a/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
+++ b/keymaster/4.0/support/include/keymasterV4_0/keymaster_tags.h
@@ -112,6 +112,14 @@
 DECLARE_TYPED_TAG(ASSOCIATED_DATA);
 DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
 DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_BRAND);
+DECLARE_TYPED_TAG(ATTESTATION_ID_DEVICE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_SERIAL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_IMEI);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MEID);
 DECLARE_TYPED_TAG(AUTH_TIMEOUT);
 DECLARE_TYPED_TAG(BLOB_USAGE_REQUIREMENTS);
 DECLARE_TYPED_TAG(BLOCK_MODE);
@@ -155,21 +163,22 @@
 template <typename... Elems>
 struct MetaList {};
 
-using all_tags_t =
-    MetaList<TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t,
-             TAG_MIN_MAC_LENGTH_t, TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t,
-             TAG_ACTIVE_DATETIME_t, TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
-             TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t,
-             TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t,
-             TAG_ALLOW_WHILE_ON_BODY_t, TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t,
-             TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t,
-             TAG_HARDWARE_TYPE_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t,
-             TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t,
-             TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t,
-             TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
-             TAG_DIGEST_t, TAG_PADDING_t, TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t,
-             TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t, TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t,
-             TAG_TRUSTED_CONFIRMATION_REQUIRED_t, TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
+using all_tags_t = MetaList<
+        TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
+        TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
+        TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+        TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USER_ID_t, TAG_USER_SECURE_ID_t,
+        TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t, TAG_ALLOW_WHILE_ON_BODY_t,
+        TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t, TAG_APPLICATION_DATA_t,
+        TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t, TAG_HARDWARE_TYPE_t,
+        TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t, TAG_BOOTLOADER_ONLY_t,
+        TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t, TAG_ATTESTATION_CHALLENGE_t,
+        TAG_ATTESTATION_APPLICATION_ID_t, TAG_ATTESTATION_ID_BRAND_t, TAG_ATTESTATION_ID_DEVICE_t,
+        TAG_ATTESTATION_ID_PRODUCT_t, TAG_ATTESTATION_ID_MANUFACTURER_t, TAG_ATTESTATION_ID_MODEL_t,
+        TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
+        TAG_DIGEST_t, TAG_PADDING_t, TAG_BLOB_USAGE_REQUIREMENTS_t, TAG_ORIGIN_t,
+        TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t, TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t,
+        TAG_TRUSTED_CONFIRMATION_REQUIRED_t, TAG_TRUSTED_USER_PRESENCE_REQUIRED_t>;
 
 template <typename TypedTagType>
 struct TypedTag2ValueType;
diff --git a/keymaster/4.0/vts/functional/Android.bp b/keymaster/4.0/vts/functional/Android.bp
index e706c68..a7be660 100644
--- a/keymaster/4.0/vts/functional/Android.bp
+++ b/keymaster/4.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalKeymasterV4_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/keymaster/4.0/vts/functional/KeymasterHidlTest.h b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
index f495516..2ca7ea7 100644
--- a/keymaster/4.0/vts/functional/KeymasterHidlTest.h
+++ b/keymaster/4.0/vts/functional/KeymasterHidlTest.h
@@ -234,6 +234,7 @@
 };
 
 #define INSTANTIATE_KEYMASTER_HIDL_TEST(name)                                      \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                           \
     INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                    \
                              testing::ValuesIn(KeymasterHidlTest::build_params()), \
                              android::hardware::PrintInstanceNameToString)
diff --git a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
index d669667..01c502c 100644
--- a/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
+++ b/keymaster/4.0/vts/functional/keymaster_hidl_hal_test.cpp
@@ -96,6 +96,18 @@
     return count > 0;
 }
 
+// If the given property is available, add it to the tag set under the given tag ID.
+template <Tag tag>
+void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
+                       const char* prop) {
+    char value[PROPERTY_VALUE_MAX];
+    int len = property_get(prop, value, /* default = */ "");
+    if (len > 0) {
+        tags->Authorization(ttag, reinterpret_cast<const uint8_t*>(value),
+                            static_cast<size_t>(len));
+    }
+}
+
 constexpr char hex_value[256] = {0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
                                  0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
                                  0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
@@ -136,6 +148,54 @@
     return retval;
 }
 
+/*
+ * DER-encoded PKCS#8 format RSA key. Generated using:
+ *
+ * openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
+ */
+string rsa_2048_key = hex2str(
+    "308204BD020100300D06092A864886F70D0101010500048204A7308204A3"
+    "0201000282010100BEBC342B56D443B1299F9A6A7056E80A897E318476A5"
+    "A18029E63B2ED739A61791D339F58DC763D9D14911F2EDEC383DEE11F631"
+    "9B44510E7A3ECD9B79B97382E49500ACF8117DC89CAF0E621F77756554A2"
+    "FD4664BFE7AB8B59AB48340DBFA27B93B5A81F6ECDEB02D0759307128DF3"
+    "E3BAD4055C8B840216DFAA5700670E6C5126F0962FCB70FF308F25049164"
+    "CCF76CC2DA66A7DD9A81A714C2809D69186133D29D84568E892B6FFBF319"
+    "9BDB14383EE224407F190358F111A949552ABA6714227D1BD7F6B20DD0CB"
+    "88F9467B719339F33BFF35B3870B3F62204E4286B0948EA348B524544B5F"
+    "9838F29EE643B079EEF8A713B220D7806924CDF7295070C5020301000102"
+    "82010069F377F35F2F584EF075353CCD1CA99738DB3DBC7C7FF35F9366CE"
+    "176DFD1B135AB10030344ABF5FBECF1D4659FDEF1C0FC430834BE1BE3911"
+    "951377BB3D563A2EA9CA8F4AD9C48A8CE6FD516A735C662686C7B4B3C09A"
+    "7B8354133E6F93F790D59EAEB92E84C9A4339302CCE28FDF04CCCAFA7DE3"
+    "F3A827D4F6F7D38E68B0EC6AB706645BF074A4E4090D06FB163124365FD5"
+    "EE7A20D350E9958CC30D91326E1B292E9EF5DB408EC42DAF737D20149704"
+    "D0A678A0FB5B5446863B099228A352D604BA8091A164D01D5AB05397C71E"
+    "AD20BE2A08FC528FE442817809C787FEE4AB97F97B9130D022153EDC6EB6"
+    "CBE7B0F8E3473F2E901209B5DB10F93604DB0102818100E83C0998214941"
+    "EA4F9293F1B77E2E99E6CF305FAF358238E126124FEAF2EB9724B2EA7B78"
+    "E6032343821A80E55D1D88FB12D220C3F41A56142FEC85796D1917F1E8C7"
+    "74F142B67D3D6E7B7E6B4383E94DB5929089DBB346D5BDAB40CC2D96EE04"
+    "09475E175C63BF78CFD744136740838127EA723FF3FE7FA368C1311B4A4E"
+    "0502818100D240FCC0F5D7715CDE21CB2DC86EA146132EA3B06F61FF2AF5"
+    "4BF38473F59DADCCE32B5F4CC32DD0BA6F509347B4B5B1B58C39F95E4798"
+    "CCBB43E83D0119ACF532F359CA743C85199F0286610E200997D731291717"
+    "9AC9B67558773212EC961E8BCE7A3CC809BC5486A96E4B0E6AF394D94E06"
+    "6A0900B7B70E82A44FB30053C102818100AD15DA1CBD6A492B66851BA8C3"
+    "16D38AB700E2CFDDD926A658003513C54BAA152B30021D667D20078F500F"
+    "8AD3E7F3945D74A891ED1A28EAD0FEEAEC8C14A8E834CF46A13D1378C99D"
+    "18940823CFDD27EC5810D59339E0C34198AC638E09C87CBB1B634A9864AE"
+    "9F4D5EB2D53514F67B4CAEC048C8AB849A02E397618F3271350281801FA2"
+    "C1A5331880A92D8F3E281C617108BF38244F16E352E69ED417C7153F9EC3"
+    "18F211839C643DCF8B4DD67CE2AC312E95178D5D952F06B1BF779F491692"
+    "4B70F582A23F11304E02A5E7565AE22A35E74FECC8B6FDC93F92A1A37703"
+    "E4CF0E63783BD02EB716A7ECBBFA606B10B74D01579522E7EF84D91FC522"
+    "292108D902C1028180796FE3825F9DCC85DF22D58690065D93898ACD65C0"
+    "87BEA8DA3A63BF4549B795E2CD0E3BE08CDEBD9FCF1720D9CDC5070D74F4"
+    "0DED8E1102C52152A31B6165F83A6722AECFCC35A493D7634664B888A08D"
+    "3EB034F12EA28BFEE346E205D334827F778B16ED40872BD29FCB36536B6E"
+    "93FFB06778696B4A9D81BB0A9423E63DE5");
+
 string rsa_key = hex2str(
     "30820275020100300d06092a864886f70d01010105000482025f3082025b"
     "02010002818100c6095409047d8634812d5a218176e45c41d60a75b13901"
@@ -873,6 +933,23 @@
                               .Authorization(TAG_MIN_MAC_LENGTH, 128)));
 }
 
+/**
+ * NewKeyGenerationTest.AesInvalidKeySize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidKeySize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AesEncryptionKey(key_size)
+                                      .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                      .Padding(PaddingMode::NONE)));
+    }
+}
+
 INSTANTIATE_KEYMASTER_HIDL_TEST(NewKeyGenerationTest);
 
 typedef KeymasterHidlTest SigningOperationsTest;
@@ -1905,21 +1982,31 @@
  * Verifies that importing and using an RSA key pair works correctly.
  */
 TEST_P(ImportKeyTest, RsaSuccess) {
+    uint32_t keysize;
+    string key;
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        keysize = 2048;
+        key = rsa_2048_key;
+    } else {
+        keysize = 1024;
+        key = rsa_key;
+    }
+
     ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
-                                           .Authorization(TAG_NO_AUTH_REQUIRED)
-                                           .RsaSigningKey(1024, 65537)
-                                           .Digest(Digest::SHA_2_256)
-                                           .Padding(PaddingMode::RSA_PSS),
-                                       KeyFormat::PKCS8, rsa_key));
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .RsaSigningKey(keysize, 65537)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_PSS),
+                                       KeyFormat::PKCS8, key));
 
     CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
-    CheckCryptoParam(TAG_KEY_SIZE, 1024U);
+    CheckCryptoParam(TAG_KEY_SIZE, keysize);
     CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
     CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
     CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
     CheckOrigin();
 
-    string message(1024 / 8, 'a');
+    string message(keysize / 8, 'a');
     auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
     string signature = SignMessage(message, params);
     VerifyMessage(message, signature, params);
@@ -2509,7 +2596,8 @@
     string message = "Hello World!";
     auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
     string ciphertext1 = EncryptMessage(message, params);
-    EXPECT_EQ(2048U / 8, ciphertext1.size());
+    // Die here on failure because we try to modify ciphertext1 below
+    ASSERT_EQ(2048U / 8, ciphertext1.size()) << "Failed to encrypt the message";
 
     string ciphertext2 = EncryptMessage(message, params);
     EXPECT_EQ(2048U / 8, ciphertext2.size());
@@ -4333,6 +4421,95 @@
 }
 
 /*
+ * AttestationTest.EcAttestationID
+ *
+ * Verifies that attesting to EC keys with correct attestation ID fields works and generates the
+ * expected output.
+ */
+TEST_P(AttestationTest, EcAttestationID) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_INCLUDE_UNIQUE_ID)));
+
+    // Collection of valid attestation ID tags.
+    auto attestation_id_tags = AuthorizationSetBuilder();
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
+                      "ro.product.manufacturer");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
+
+    for (const KeyParameter& tag : attestation_id_tags) {
+        AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo"));
+        // Include one of the (valid) attestation ID tags.
+        builder.push_back(tag);
+        hidl_vec<hidl_vec<uint8_t>> cert_chain;
+        auto result = AttestKey(builder, &cert_chain);
+        if (result == ErrorCode::CANNOT_ATTEST_IDS) {
+            continue;
+        }
+
+        ASSERT_EQ(ErrorCode::OK, result);
+        EXPECT_GE(cert_chain.size(), 2U);
+
+        std::vector<KeyParameter> expected_hw_enforced = key_characteristics_.hardwareEnforced;
+        expected_hw_enforced.push_back(tag);
+
+        EXPECT_TRUE(verify_attestation_record(
+                "challenge", "foo", key_characteristics_.softwareEnforced,
+                hidl_vec<KeyParameter>(expected_hw_enforced), SecLevel(), cert_chain[0]));
+    }
+}
+
+/*
+ * AttestationTest.EcAttestationMismatchID
+ *
+ * Verifies that attesting to EC keys with incorrect attestation ID fields fails.
+ */
+TEST_P(AttestationTest, EcAttestationMismatchID) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_INCLUDE_UNIQUE_ID)));
+
+    // Collection of invalid attestation ID tags.
+    std::string invalid = "completely-invalid";
+    auto invalid_tags =
+            AuthorizationSetBuilder()
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_BRAND, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_DEVICE, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_PRODUCT, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_SERIAL, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_IMEI, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MEID, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MANUFACTURER, invalid.data(),
+                                   invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MODEL, invalid.data(), invalid.size());
+
+    for (const KeyParameter& invalid_tag : invalid_tags) {
+        AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_ATTESTATION_CHALLENGE, HidlBuf("challenge"))
+                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, HidlBuf("foo"));
+        // Include one of the invalid attestation ID tags.
+        builder.push_back(invalid_tag);
+        hidl_vec<hidl_vec<uint8_t>> cert_chain;
+        auto result = AttestKey(builder, &cert_chain);
+
+        EXPECT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
+                << "result: " << static_cast<int32_t>(result);
+    }
+}
+
+/*
  * AttestationTest.EcAttestationRequiresAttestationAppId
  *
  * Verifies that attesting to EC keys requires app ID
@@ -4564,7 +4741,7 @@
  * that aborting the operations clears the operations.
  *
  */
-TEST_P(ClearOperationsTest, TooManyOperations) {
+TEST_P(ClearOperationsTest, DISABLED_TooManyOperations) {
     ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
                                              .Authorization(TAG_NO_AUTH_REQUIRED)
                                              .RsaEncryptionKey(2048, 65537)
diff --git a/keymaster/4.0/vts/performance/Android.bp b/keymaster/4.0/vts/performance/Android.bp
index 9434bc9..d7342ad 100644
--- a/keymaster/4.0/vts/performance/Android.bp
+++ b/keymaster/4.0/vts/performance/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_benchmark {
     name: "keymaster_benchmark",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/keymaster/4.1/Android.bp b/keymaster/4.1/Android.bp
index f6ac6f8..e76afbd 100644
--- a/keymaster/4.1/Android.bp
+++ b/keymaster/4.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.keymaster@4.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IKeymasterDevice.hal",
diff --git a/keymaster/4.1/IKeymasterDevice.hal b/keymaster/4.1/IKeymasterDevice.hal
index bbeccaa..ccb9f2e 100644
--- a/keymaster/4.1/IKeymasterDevice.hal
+++ b/keymaster/4.1/IKeymasterDevice.hal
@@ -37,6 +37,7 @@
  * versions will be numbered as major_version * 10 + minor version.  The addition of new attestable
  * tags changes the attestation format again, slightly, so the attestationVersion must be 4.
  */
+@SensitiveData
 interface IKeymasterDevice extends @4.0::IKeymasterDevice {
     /**
      * Called by client to notify the IKeymasterDevice that the device is now locked, and keys with
diff --git a/keymaster/4.1/default/Android.bp b/keymaster/4.1/default/Android.bp
index 3442b18..6ec1fae 100644
--- a/keymaster/4.1/default/Android.bp
+++ b/keymaster/4.1/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.keymaster@4.1-service",
     defaults: ["hidl_defaults"],
@@ -36,5 +45,14 @@
         "liblog",
         "libutils",
     ],
+    required: [
+        "android.hardware.hardware_keystore.km41.xml",
+    ],
+}
 
+prebuilt_etc {
+    name: "android.hardware.hardware_keystore.km41.xml",
+    sub_dir: "permissions",
+    vendor: true,
+    src: "android.hardware.hardware_keystore.km41.xml",
 }
diff --git a/keymaster/4.1/default/OWNERS b/keymaster/4.1/default/OWNERS
index 335660d..2b2ad2a 100644
--- a/keymaster/4.1/default/OWNERS
+++ b/keymaster/4.1/default/OWNERS
@@ -1,2 +1,4 @@
+jbires@google.com
 jdanis@google.com
 swillden@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml b/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml
new file mode 100644
index 0000000..0dbeed8
--- /dev/null
+++ b/keymaster/4.1/default/android.hardware.hardware_keystore.km41.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<permissions>
+  <feature name="android.hardware.hardware_keystore" version="41" />
+</permissions>
diff --git a/keymaster/4.1/support/Android.bp b/keymaster/4.1/support/Android.bp
index bdd0ca8..fe0d07d 100644
--- a/keymaster/4.1/support/Android.bp
+++ b/keymaster/4.1/support/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "libkeymaster4_1support",
     vendor_available: true,
diff --git a/keymaster/4.1/support/OWNERS b/keymaster/4.1/support/OWNERS
index a9efe66..2b2ad2a 100644
--- a/keymaster/4.1/support/OWNERS
+++ b/keymaster/4.1/support/OWNERS
@@ -1,3 +1,4 @@
+jbires@google.com
 jdanis@google.com
 swillden@google.com
-jbires@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/support/attestation_record.cpp b/keymaster/4.1/support/attestation_record.cpp
index 207a7e8..15230d5 100644
--- a/keymaster/4.1/support/attestation_record.cpp
+++ b/keymaster/4.1/support/attestation_record.cpp
@@ -79,6 +79,8 @@
     ASN1_INTEGER_SET* padding;
     ASN1_INTEGER* ec_curve;
     ASN1_INTEGER* rsa_public_exponent;
+    ASN1_NULL* rollback_resistance;
+    ASN1_NULL* early_boot_only;
     ASN1_INTEGER* active_date_time;
     ASN1_INTEGER* origination_expire_date_time;
     ASN1_INTEGER* usage_expire_date_time;
@@ -86,21 +88,27 @@
     ASN1_INTEGER* user_auth_type;
     ASN1_INTEGER* auth_timeout;
     ASN1_NULL* allow_while_on_body;
+    ASN1_NULL* trusted_user_presence_required;
+    ASN1_NULL* trusted_confirmation_required;
+    ASN1_NULL* unlocked_device_required;
     ASN1_NULL* all_applications;
     ASN1_OCTET_STRING* application_id;
     ASN1_INTEGER* creation_date_time;
     ASN1_INTEGER* origin;
-    ASN1_NULL* rollback_resistance;
     KM_ROOT_OF_TRUST* root_of_trust;
     ASN1_INTEGER* os_version;
     ASN1_INTEGER* os_patchlevel;
     ASN1_OCTET_STRING* attestation_application_id;
-    ASN1_NULL* trusted_user_presence_required;
-    ASN1_NULL* trusted_confirmation_required;
-    ASN1_NULL* unlocked_device_required;
+    ASN1_OCTET_STRING* attestation_id_brand;
+    ASN1_OCTET_STRING* attestation_id_device;
+    ASN1_OCTET_STRING* attestation_id_product;
+    ASN1_OCTET_STRING* attestation_id_serial;
+    ASN1_OCTET_STRING* attestation_id_imei;
+    ASN1_OCTET_STRING* attestation_id_meid;
+    ASN1_OCTET_STRING* attestation_id_manufacturer;
+    ASN1_OCTET_STRING* attestation_id_model;
     ASN1_INTEGER* vendor_patchlevel;
     ASN1_INTEGER* boot_patchlevel;
-    ASN1_NULL* early_boot_only;
     ASN1_NULL* device_unique_attestation;
     ASN1_NULL* identity_credential_key;
 } KM_AUTH_LIST;
@@ -116,6 +124,7 @@
                      TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
                      TAG_ROLLBACK_RESISTANCE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
                      TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
@@ -138,12 +147,27 @@
         ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_brand, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_BRAND.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_device, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_DEVICE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_product, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_PRODUCT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_serial, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_SERIAL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_imei, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_IMEI.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_meid, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MEID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_manufacturer, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MANUFACTURER.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_model, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MODEL.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
                      TAG_VENDOR_PATCHLEVEL.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
-        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
-                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
-        ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
                      TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
         ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential_key, ASN1_NULL,
@@ -279,6 +303,14 @@
     copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
     copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
     copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+    copyAuthTag(record->attestation_id_brand, TAG_ATTESTATION_ID_BRAND, auth_list);
+    copyAuthTag(record->attestation_id_device, TAG_ATTESTATION_ID_DEVICE, auth_list);
+    copyAuthTag(record->attestation_id_product, TAG_ATTESTATION_ID_PRODUCT, auth_list);
+    copyAuthTag(record->attestation_id_serial, TAG_ATTESTATION_ID_SERIAL, auth_list);
+    copyAuthTag(record->attestation_id_imei, TAG_ATTESTATION_ID_IMEI, auth_list);
+    copyAuthTag(record->attestation_id_meid, TAG_ATTESTATION_ID_MEID, auth_list);
+    copyAuthTag(record->attestation_id_manufacturer, TAG_ATTESTATION_ID_MANUFACTURER, auth_list);
+    copyAuthTag(record->attestation_id_model, TAG_ATTESTATION_ID_MODEL, auth_list);
     copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
     copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
     copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
diff --git a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
index 40eb142..e8db56a 100644
--- a/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
+++ b/keymaster/4.1/support/include/keymasterV4_1/keymaster_tags.h
@@ -53,6 +53,14 @@
 using V4_0::TAG_ASSOCIATED_DATA;
 using V4_0::TAG_ATTESTATION_APPLICATION_ID;
 using V4_0::TAG_ATTESTATION_CHALLENGE;
+using V4_0::TAG_ATTESTATION_ID_BRAND;
+using V4_0::TAG_ATTESTATION_ID_DEVICE;
+using V4_0::TAG_ATTESTATION_ID_IMEI;
+using V4_0::TAG_ATTESTATION_ID_MANUFACTURER;
+using V4_0::TAG_ATTESTATION_ID_MEID;
+using V4_0::TAG_ATTESTATION_ID_MODEL;
+using V4_0::TAG_ATTESTATION_ID_PRODUCT;
+using V4_0::TAG_ATTESTATION_ID_SERIAL;
 using V4_0::TAG_AUTH_TIMEOUT;
 using V4_0::TAG_BLOB_USAGE_REQUIREMENTS;
 using V4_0::TAG_BLOCK_MODE;
diff --git a/keymaster/4.1/vts/OWNERS b/keymaster/4.1/vts/OWNERS
index 335660d..2b2ad2a 100644
--- a/keymaster/4.1/vts/OWNERS
+++ b/keymaster/4.1/vts/OWNERS
@@ -1,2 +1,4 @@
+jbires@google.com
 jdanis@google.com
 swillden@google.com
+zeuthen@google.com
\ No newline at end of file
diff --git a/keymaster/4.1/vts/functional/Android.bp b/keymaster/4.1/vts/functional/Android.bp
index 5ba05ea..c650bec 100644
--- a/keymaster/4.1/vts/functional/Android.bp
+++ b/keymaster/4.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalKeymasterV4_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
index 728a523..4a57f44 100644
--- a/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
+++ b/keymaster/4.1/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -16,6 +16,7 @@
 
 #define LOG_TAG "keymaster_hidl_hal_test"
 #include <cutils/log.h>
+#include <vector>
 
 #include "Keymaster4_1HidlTest.h"
 
@@ -26,6 +27,8 @@
 #include <keymasterV4_1/attestation_record.h>
 #include <keymasterV4_1/authorization_set.h>
 
+using android::hardware::keymaster::V4_0::test::add_tag_from_prop;
+
 // Not to dump the attestation by default. Can enable by specify the parameter
 // "--dump_attestations" on lunching VTS
 static bool dumpAttestations = false;
@@ -172,10 +175,42 @@
     attestation.software_enforced.Sort();
     attestation.hardware_enforced.Sort();
 
-    EXPECT_EQ(filter_tags(expected_sw_enforced), filter_tags(attestation.software_enforced))
-            << DIFFERENCE(expected_sw_enforced, attestation.software_enforced);
-    EXPECT_EQ(filter_tags(expected_hw_enforced), filter_tags(attestation.hardware_enforced))
-            << DIFFERENCE(expected_hw_enforced, attestation.hardware_enforced);
+    expected_sw_enforced = filter_tags(expected_sw_enforced);
+    expected_hw_enforced = filter_tags(expected_hw_enforced);
+    AuthorizationSet attestation_sw_enforced = filter_tags(attestation.software_enforced);
+    AuthorizationSet attestation_hw_enforced = filter_tags(attestation.hardware_enforced);
+
+    EXPECT_EQ(expected_sw_enforced, attestation_sw_enforced)
+            << DIFFERENCE(expected_sw_enforced, attestation_sw_enforced);
+    EXPECT_EQ(expected_hw_enforced, attestation_hw_enforced)
+            << DIFFERENCE(expected_hw_enforced, attestation_hw_enforced);
+}
+
+X509_Ptr parse_cert_blob(const std::vector<uint8_t>& blob) {
+    const uint8_t* p = blob.data();
+    return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
+}
+
+bool check_certificate_chain_signatures(const hidl_vec<hidl_vec<uint8_t>>& cert_chain) {
+    // TODO: Check that root is self-signed once b/187803288 is resolved.
+    for (size_t i = 0; i < cert_chain.size() - 1; ++i) {
+        X509_Ptr key_cert(parse_cert_blob(cert_chain[i]));
+        X509_Ptr signing_cert(parse_cert_blob(cert_chain[i + 1]));
+
+        if (!key_cert.get() || !signing_cert.get()) {
+            return false;
+        }
+
+        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+        if (!signing_pubkey.get()) {
+            return false;
+        }
+
+        if (!X509_verify(key_cert.get(), signing_pubkey.get())) {
+            return false;
+        }
+    }
+    return true;
 }
 
 }  // namespace
@@ -221,71 +256,84 @@
 
 TEST_P(DeviceUniqueAttestationTest, Rsa) {
     if (SecLevel() != SecurityLevel::STRONGBOX) return;
-    ASSERT_EQ(ErrorCode::OK,
-              convert(GenerateKey(AuthorizationSetBuilder()
-                                  .Authorization(TAG_NO_AUTH_REQUIRED)
-                                  .RsaSigningKey(2048, 65537)
-                                  .Digest(Digest::SHA_2_256)
-                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-                                  .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+    ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+                                                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                         .RsaSigningKey(2048, 65537)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                         .Authorization(TAG_INCLUDE_UNIQUE_ID))));
 
     hidl_vec<hidl_vec<uint8_t>> cert_chain;
     HidlBuf challenge("challenge");
     HidlBuf app_id("foo");
-    EXPECT_EQ(ErrorCode::OK,
-              convert(AttestKey(AuthorizationSetBuilder()
-                                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
-                                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
-                                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
-                                &cert_chain)));
+    ErrorCode result =
+            convert(AttestKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                                      .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                                      .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+                              &cert_chain));
 
+    // It is optional for Strong box to support DeviceUniqueAttestation.
+    if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+
+    EXPECT_EQ(ErrorCode::OK, result);
     EXPECT_EQ(2U, cert_chain.size());
-    if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
+    EXPECT_TRUE(check_certificate_chain_signatures(cert_chain));
+    if (dumpAttestations) {
+      for (auto cert_ : cert_chain) dumpContent(bin2hex(cert_));
+    }
     auto [err, attestation] = parse_attestation_record(cert_chain[0]);
     ASSERT_EQ(ErrorCode::OK, err);
 
-    check_attestation_record(attestation, challenge,
-                             /* sw_enforced */
-                             AuthorizationSetBuilder()
-                                     .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
-                             /* hw_enforced */
-                             AuthorizationSetBuilder()
-                                     .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
-                                     .Authorization(TAG_NO_AUTH_REQUIRED)
-                                     .RsaSigningKey(2048, 65537)
-                                     .Digest(Digest::SHA_2_256)
-                                     .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-                                     .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
-                                     .Authorization(TAG_OS_VERSION, os_version())
-                                     .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
-                             SecLevel());
+    check_attestation_record(
+            attestation, challenge,
+            /* sw_enforced */
+            AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+            /* hw_enforced */
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                    .RsaSigningKey(2048, 65537)
+                    .Digest(Digest::SHA_2_256)
+                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                    .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                    .Authorization(TAG_OS_VERSION, os_version())
+                    .Authorization(TAG_OS_PATCHLEVEL, os_patch_level()),
+            SecLevel());
 }
 
 TEST_P(DeviceUniqueAttestationTest, Ecdsa) {
     if (SecLevel() != SecurityLevel::STRONGBOX) return;
-    ASSERT_EQ(ErrorCode::OK,
-              convert(GenerateKey(AuthorizationSetBuilder()
-                                          .Authorization(TAG_NO_AUTH_REQUIRED)
-                                          .EcdsaSigningKey(256)
-                                          .Digest(Digest::SHA_2_256)
-                                          .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+    ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+                                                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                         .EcdsaSigningKey(256)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_INCLUDE_UNIQUE_ID))));
 
     hidl_vec<hidl_vec<uint8_t>> cert_chain;
     HidlBuf challenge("challenge");
     HidlBuf app_id("foo");
-    EXPECT_EQ(ErrorCode::OK,
-              convert(AttestKey(AuthorizationSetBuilder()
-                                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
-                                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
-                                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
-                                &cert_chain)));
+    ErrorCode result =
+            convert(AttestKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                                      .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                                      .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+                              &cert_chain));
 
+    // It is optional for Strong box to support DeviceUniqueAttestation.
+    if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+
+    EXPECT_EQ(ErrorCode::OK, result);
     EXPECT_EQ(2U, cert_chain.size());
-    if (dumpAttestations) dumpContent(bin2hex(cert_chain[0]));
+    EXPECT_TRUE(check_certificate_chain_signatures(cert_chain));
+    if (dumpAttestations) {
+      for (auto cert_ : cert_chain) dumpContent(bin2hex(cert_));
+    }
     auto [err, attestation] = parse_attestation_record(cert_chain[0]);
     ASSERT_EQ(ErrorCode::OK, err);
 
-    check_attestation_record(attestation, challenge,
+    check_attestation_record(
+            attestation, challenge,
             /* sw_enforced */
             AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
             /* hw_enforced */
@@ -301,6 +349,106 @@
             SecLevel());
 }
 
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationID) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+                                                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                         .EcdsaSigningKey(256)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+
+    // Collection of valid attestation ID tags.
+    auto attestation_id_tags = AuthorizationSetBuilder();
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_MANUFACTURER,
+                      "ro.product.manufacturer");
+    add_tag_from_prop(&attestation_id_tags, V4_0::TAG_ATTESTATION_ID_MODEL, "ro.product.model");
+
+    for (const KeyParameter& tag : attestation_id_tags) {
+        hidl_vec<hidl_vec<uint8_t>> cert_chain;
+        HidlBuf challenge("challenge");
+        HidlBuf app_id("foo");
+        AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id);
+        builder.push_back(tag);
+        ErrorCode result = convert(AttestKey(builder, &cert_chain));
+
+        // It is optional for Strong box to support DeviceUniqueAttestation.
+        if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+
+        ASSERT_EQ(ErrorCode::OK, result);
+        EXPECT_EQ(2U, cert_chain.size());
+        if (dumpAttestations) {
+            for (auto cert_ : cert_chain) dumpContent(bin2hex(cert_));
+        }
+        auto [err, attestation] = parse_attestation_record(cert_chain[0]);
+        ASSERT_EQ(ErrorCode::OK, err);
+
+        AuthorizationSetBuilder hw_enforced =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                        .EcdsaSigningKey(256)
+                        .Digest(Digest::SHA_2_256)
+                        .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                        .Authorization(TAG_OS_VERSION, os_version())
+                        .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+        hw_enforced.push_back(tag);
+        check_attestation_record(
+                attestation, challenge,
+                /* sw_enforced */
+                AuthorizationSetBuilder().Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id),
+                hw_enforced, SecLevel());
+    }
+}
+
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationMismatchID) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, convert(GenerateKey(AuthorizationSetBuilder()
+                                                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                         .EcdsaSigningKey(256)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_INCLUDE_UNIQUE_ID))));
+
+    // Collection of invalid attestation ID tags.
+    std::string invalid = "completely-invalid";
+    auto attestation_id_tags =
+            AuthorizationSetBuilder()
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_BRAND, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_DEVICE, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_PRODUCT, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_SERIAL, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_IMEI, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MEID, invalid.data(), invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MANUFACTURER, invalid.data(),
+                                   invalid.size())
+                    .Authorization(V4_0::TAG_ATTESTATION_ID_MODEL, invalid.data(), invalid.size());
+
+    for (const KeyParameter& invalid_tag : attestation_id_tags) {
+        hidl_vec<hidl_vec<uint8_t>> cert_chain;
+        HidlBuf challenge("challenge");
+        HidlBuf app_id("foo");
+        AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, app_id);
+        builder.push_back(invalid_tag);
+        ErrorCode result = convert(AttestKey(builder, &cert_chain));
+
+        EXPECT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
+                << "result: " << static_cast<int32_t>(result);
+    }
+}
+
 INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(DeviceUniqueAttestationTest);
 
 }  // namespace test
diff --git a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
index 152c063..670ccfb 100644
--- a/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
+++ b/keymaster/4.1/vts/functional/Keymaster4_1HidlTest.h
@@ -18,6 +18,8 @@
 
 #include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
 
+#include <android-base/properties.h>
+
 #include <KeymasterHidlTest.h>
 #include <keymasterV4_1/authorization_set.h>
 
@@ -153,8 +155,23 @@
 }
 
 #define INSTANTIATE_KEYMASTER_4_1_HIDL_TEST(name)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);                              \
     INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                       \
                              testing::ValuesIn(Keymaster4_1HidlTest::build_params()), \
                              android::hardware::PrintInstanceNameToString)
 
 }  // namespace android::hardware::keymaster::V4_1::test
+
+namespace android::hardware::keymaster::V4_0::test {
+
+// If the given property is available, add it to the tag set under the given tag ID.
+template <Tag tag>
+void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
+                       const char* prop) {
+    std::string prop_value = ::android::base::GetProperty(prop, /* default= */ "");
+    if (!prop_value.empty()) {
+        tags->Authorization(ttag, prop_value.data(), prop_value.size());
+    }
+}
+
+}  // namespace android::hardware::keymaster::V4_0::test
diff --git a/keymaster/aidl/Android.bp b/keymaster/aidl/Android.bp
index 56a3ca9..b7a261c 100644
--- a/keymaster/aidl/Android.bp
+++ b/keymaster/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.keymaster",
     vendor_available: true,
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
index db1df2b..4f21cba 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -21,7 +21,7 @@
   long challenge;
   long userId;
   long authenticatorId;
-  android.hardware.keymaster.HardwareAuthenticatorType authenticatorType;
+  android.hardware.keymaster.HardwareAuthenticatorType authenticatorType = android.hardware.keymaster.HardwareAuthenticatorType.NONE;
   android.hardware.keymaster.Timestamp timestamp;
   byte[] mac;
 }
diff --git a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
index 0633765..b116dac 100644
--- a/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/aidl_api/android.hardware.keymaster/current/android/hardware/keymaster/VerificationToken.aidl
@@ -20,6 +20,6 @@
 parcelable VerificationToken {
   long challenge;
   android.hardware.keymaster.Timestamp timestamp;
-  android.hardware.keymaster.SecurityLevel securityLevel;
+  android.hardware.keymaster.SecurityLevel securityLevel = android.hardware.keymaster.SecurityLevel.SOFTWARE;
   byte[] mac;
 }
diff --git a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
index 58602aa..99b036a 100644
--- a/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/HardwareAuthToken.aidl
@@ -55,7 +55,7 @@
      * authenticatorType describes the type of authentication that took place, e.g. password or
      * fingerprint.
      */
-    HardwareAuthenticatorType authenticatorType;
+    HardwareAuthenticatorType authenticatorType = HardwareAuthenticatorType.NONE;
 
     /**
      * timestamp indicates when the user authentication took place, in milliseconds since some
diff --git a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
index f129783..00578a4 100644
--- a/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/SecurityLevel.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * 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.
diff --git a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
index 4b2f108..19ea944 100644
--- a/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/Timestamp.aidl
@@ -16,6 +16,14 @@
 
 package android.hardware.keymaster;
 
+
+/**
+ * Time in milliseconds since some arbitrary point in time.  Time must be monotonically increasing,
+ * and a secure environment's notion of "current time" must not repeat until the Android device
+ * reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
+ * by setting the clock to zero during each boot, and then counting time accurately).
+ */
+
 @VintfStability
 parcelable Timestamp {
     long milliSeconds;
diff --git a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
index eff9ca6..5efd937 100644
--- a/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
+++ b/keymaster/aidl/android/hardware/keymaster/VerificationToken.aidl
@@ -1,5 +1,5 @@
 /*
- * Copyright 2020 The Android Open Source Project
+ * 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.
@@ -18,7 +18,6 @@
 
 import android.hardware.keymaster.SecurityLevel;
 import android.hardware.keymaster.Timestamp;
-import android.hardware.keymaster.HardwareAuthenticatorType;
 
 /**
  * VerificationToken instances are used for secure environments to authenticate one another.
@@ -40,10 +39,11 @@
      */
     Timestamp timestamp;
 
+
     /**
      * SecurityLevel of the secure environment that generated the token.
      */
-    SecurityLevel securityLevel;
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
 
     /**
      * 32-byte HMAC-SHA256 of the above values, computed as:
diff --git a/light/2.0/Android.bp b/light/2.0/Android.bp
index d51f10d..95eb5ae 100644
--- a/light/2.0/Android.bp
+++ b/light/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.light@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ILight.hal",
diff --git a/light/2.0/default/Android.bp b/light/2.0/default/Android.bp
index ed48825..2745f3d 100644
--- a/light/2.0/default/Android.bp
+++ b/light/2.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.light@2.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/light/2.0/default/Light.cpp b/light/2.0/default/Light.cpp
index 5484d2d..3febf6b 100644
--- a/light/2.0/default/Light.cpp
+++ b/light/2.0/default/Light.cpp
@@ -140,7 +140,7 @@
         ret = hwModule->methods->open(hwModule, name,
             reinterpret_cast<hw_device_t**>(&lightDevice));
         if (ret != 0) {
-            ALOGE("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
+            ALOGI("light_open %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
         }
     } else {
         ALOGE("hw_get_module %s %s failed: %d", LIGHTS_HARDWARE_MODULE_ID, name, ret);
diff --git a/light/2.0/vts/functional/Android.bp b/light/2.0/vts/functional/Android.bp
index 06590c3..91fb847 100644
--- a/light/2.0/vts/functional/Android.bp
+++ b/light/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalLightV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.light@2.0"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/light/2.0/vts/functional/VtsHalLightV2_0TargetTest.cpp b/light/2.0/vts/functional/VtsHalLightV2_0TargetTest.cpp
index 6fcecd2..f65005e 100644
--- a/light/2.0/vts/functional/VtsHalLightV2_0TargetTest.cpp
+++ b/light/2.0/vts/functional/VtsHalLightV2_0TargetTest.cpp
@@ -150,6 +150,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LightHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, LightHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ILight::descriptor)),
diff --git a/light/aidl/Android.bp b/light/aidl/Android.bp
index 91de3e9..c8973f3 100644
--- a/light/aidl/Android.bp
+++ b/light/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.light",
     vendor_available: true,
@@ -7,7 +16,7 @@
     stability: "vintf",
     backend: {
         java: {
-            platform_apis: true,
+            sdk_version: "module_current",
         },
         ndk: {
             vndk: {
diff --git a/light/aidl/default/Android.bp b/light/aidl/default/Android.bp
index ae3f463..459b8e2 100644
--- a/light/aidl/default/Android.bp
+++ b/light/aidl/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.lights-service.example",
     relative_install_path: "hw",
@@ -7,7 +16,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.light-ndk_platform",
+        "android.hardware.light-V1-ndk_platform",
     ],
     srcs: [
         "Lights.cpp",
diff --git a/light/aidl/vts/functional/Android.bp b/light/aidl/vts/functional/Android.bp
index aa4719b..c5a8562 100644
--- a/light/aidl/vts/functional/Android.bp
+++ b/light/aidl/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalLightTargetTest",
     defaults: [
@@ -27,7 +36,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.light-cpp",
+        "android.hardware.light-V1-cpp",
     ],
     test_suites: [
         "vts",
diff --git a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
index 3c26278..5e3eabd 100644
--- a/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
+++ b/light/aidl/vts/functional/VtsHalLightTargetTest.cpp
@@ -159,6 +159,7 @@
     EXPECT_TRUE(status.exceptionCode() == Status::EX_UNSUPPORTED_OPERATION);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(LightsAidl);
 INSTANTIATE_TEST_SUITE_P(Lights, LightsAidl,
                          testing::ValuesIn(android::getAidlHalInstanceNames(ILights::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/light/utils/Android.bp b/light/utils/Android.bp
index e901129..927394e 100644
--- a/light/utils/Android.bp
+++ b/light/utils/Android.bp
@@ -16,6 +16,15 @@
 
 // Turns off the screen.
 // Canonical usage is for init (which can't talk to hals directly).
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "blank_screen",
     init_rc: ["blank_screen.rc"],
@@ -28,6 +37,6 @@
         "libutils",
     ],
     static_libs: [
-        "android.hardware.light-cpp",
+        "android.hardware.light-V1-cpp",
     ],
 }
diff --git a/media/1.0/Android.bp b/media/1.0/Android.bp
index 2dbe466..6e823f6 100644
--- a/media/1.0/Android.bp
+++ b/media/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media@1.0",
     root: "android.hardware",
diff --git a/media/1.0/xml/Android.mk b/media/1.0/xml/Android.mk
index bc44b9e..a795288 100644
--- a/media/1.0/xml/Android.mk
+++ b/media/1.0/xml/Android.mk
@@ -6,9 +6,11 @@
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := media_profiles_V1_0.dtd
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_SRC_FILES := media_profiles.dtd
 LOCAL_MODULE_CLASS := ETC
 LOCAL_MODULE_PATH := $(TARGET_OUT_ETC)
 
 include $(BUILD_PREBUILT)
-
diff --git a/media/Android.bp b/media/Android.bp
index 267c02b..b70da2a 100644
--- a/media/Android.bp
+++ b/media/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 filegroup {
     name: "media_omx_audio_res",
     path: "res",
diff --git a/media/bufferpool/1.0/Android.bp b/media/bufferpool/1.0/Android.bp
index 079e47f..175b8a5 100644
--- a/media/bufferpool/1.0/Android.bp
+++ b/media/bufferpool/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media.bufferpool@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IAccessor.hal",
diff --git a/media/bufferpool/2.0/Android.bp b/media/bufferpool/2.0/Android.bp
index 6a82616..01273c1 100644
--- a/media/bufferpool/2.0/Android.bp
+++ b/media/bufferpool/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media.bufferpool@2.0",
     root: "android.hardware",
diff --git a/media/c2/1.0/Android.bp b/media/c2/1.0/Android.bp
index 391e6c4..dc56fef 100644
--- a/media/c2/1.0/Android.bp
+++ b/media/c2/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media.c2@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IComponent.hal",
diff --git a/media/c2/1.1/Android.bp b/media/c2/1.1/Android.bp
index c3e30b2..885a4c8 100644
--- a/media/c2/1.1/Android.bp
+++ b/media/c2/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media.c2@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IComponent.hal",
         "IComponentStore.hal",
diff --git a/media/omx/1.0/Android.bp b/media/omx/1.0/Android.bp
index 5fe73ab..e33bab3 100644
--- a/media/omx/1.0/Android.bp
+++ b/media/omx/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.media.omx@1.0",
     root: "android.hardware",
diff --git a/media/omx/1.0/vts/functional/README.md b/media/omx/1.0/vts/functional/README.md
index c5a6867..67f4aa5 100644
--- a/media/omx/1.0/vts/functional/README.md
+++ b/media/omx/1.0/vts/functional/README.md
@@ -1,12 +1,12 @@
 ## Omx Hal @ 1.0 tests ##
 ---
 ## Overview :
-The scope of the tests presented here is not restricted solely to testing omx hal @ 1.0 API but also test to omx core functionality and to an extent omx components as well. The current directory contains the following folders: audio, common, component, master and video. Besides common all other folders contain test fixtures for testing AV decoder, encoder components. Common constitutes files that are used across by these test applications.
+The scope of the tests presented here is not restricted solely to testing omx hal @ 1.0 API but also test to omx core functionality and to an extent omx components as well. The current directory contains the following folders: audio, common, component, store and video. Besides common all other folders contain test fixtures for testing AV decoder, encoder components. Common constitutes files that are used across by these test applications.
 
-#### master :
-Functionality of master is to enumerate all the omx components (and the roles it supports) available in android media framework.
+#### store :
+Functionality of store is to enumerate all the omx components (and the roles it supports) available in android media framework.
 
-usage: atest VtsHalMediaOmxV1\_0TargetMasterTest
+usage: atest VtsHalMediaOmxV1\_0TargetStoreTest
 
 #### component :
 This folder includes test fixtures that tests aspects common to all omx compatible components. For instance, port enabling/disabling, enumerating port formats, state transitions, flush, ..., stay common to all components irrespective of the service they offer. Test fixtures here are directed towards testing these (omx core). Every standard OMX compatible component is expected to pass these tests.
diff --git a/media/omx/1.0/vts/functional/audio/Android.bp b/media/omx/1.0/vts/functional/audio/Android.bp
index ec7357c..a2733c9 100644
--- a/media/omx/1.0/vts/functional/audio/Android.bp
+++ b/media/omx/1.0/vts/functional/audio/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalMediaOmxV1_0TargetAudioEncTest",
     stem: "vts_hal_media_omx_v1_0_audio_enc_test",
diff --git a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
index 3ed5670..e7dfce3 100644
--- a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
+++ b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioDecTest.cpp
@@ -1152,6 +1152,7 @@
                                                     kPortIndexOutput));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioDecHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, AudioDecHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp
index 32e6f4c..231d758 100644
--- a/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp
+++ b/media/omx/1.0/vts/functional/audio/VtsHalMediaOmxV1_0TargetAudioEncTest.cpp
@@ -486,6 +486,7 @@
                                                     kPortIndexOutput));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AudioEncHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, AudioEncHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/media/omx/1.0/vts/functional/common/Android.bp b/media/omx/1.0/vts/functional/common/Android.bp
index 064aaa5..12b6fb2 100644
--- a/media/omx/1.0/vts/functional/common/Android.bp
+++ b/media/omx/1.0/vts/functional/common/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalMediaOmxV1_0CommonUtil",
     srcs: ["media_hidl_test_common.cpp"],
@@ -59,11 +68,11 @@
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
         "libhidlmemory",
-        "libnativehelper",
     ],
 
     // TODO(b/64437680): Assume these libs are always available on the device.
     shared_libs: [
+        "libnativehelper",
         "libui",
         "libstagefright_foundation",
         "libstagefright_omx_utils",
diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
index 64f27e8..ea29f03 100644
--- a/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
+++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.cpp
@@ -215,6 +215,7 @@
     ASSERT_NE(handle, nullptr);
 
     *nStride = static_cast<int32_t>(stride);
+    buffer->handle = handle;
     buffer->omxBuffer.nativeHandle = handle;
     buffer->omxBuffer.attr.anwBuffer.width = nFrameWidth;
     buffer->omxBuffer.attr.anwBuffer.height = nFrameHeight;
@@ -335,6 +336,18 @@
     }
 }
 
+// free buffers needed on a component port
+void freePortBuffers(android::Vector<BufferInfo>* buffArray, PortMode portMode, bool allocGrap) {
+    for (size_t i = 0; i < buffArray->size(); i++) {
+        if (portMode == PortMode::PRESET_ANW_BUFFER ||
+            (allocGrap && portMode == PortMode::DYNAMIC_ANW_BUFFER)) {
+            android::GraphicBufferAllocator& allocator = android::GraphicBufferAllocator::get();
+            android::status_t error = allocator.free((*buffArray)[i].handle);
+            ASSERT_EQ(error, android::NO_ERROR);
+        }
+    }
+}
+
 // State Transition : Loaded -> Idle
 // Note: This function does not make any background checks for this transition.
 // The callee holds the reponsibility to ensure the legality of the transition.
@@ -399,11 +412,15 @@
 // The callee holds the reponsibility to ensure the legality of the transition.
 void changeStateIdletoLoaded(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
                              android::Vector<BufferInfo>* iBuffer,
-                             android::Vector<BufferInfo>* oBuffer,
-                             OMX_U32 kPortIndexInput,
-                             OMX_U32 kPortIndexOutput) {
+                             android::Vector<BufferInfo>* oBuffer, OMX_U32 kPortIndexInput,
+                             OMX_U32 kPortIndexOutput, PortMode* portMode, bool allocGrap) {
     android::hardware::media::omx::V1_0::Status status;
     Message msg;
+    PortMode defaultPortMode[2], *pm;
+
+    defaultPortMode[0] = PortMode::PRESET_BYTE_BUFFER;
+    defaultPortMode[1] = PortMode::PRESET_BYTE_BUFFER;
+    pm = portMode ? portMode : defaultPortMode;
 
     // set state to Loaded
     status = omxNode->sendCommand(toRawCommandType(OMX_CommandStateSet),
@@ -446,6 +463,8 @@
     ASSERT_EQ(msg.data.eventData.data1, OMX_CommandStateSet);
     ASSERT_EQ(msg.data.eventData.data2, OMX_StateLoaded);
 
+    ASSERT_NO_FATAL_FAILURE(freePortBuffers(iBuffer, pm[0], allocGrap));
+    ASSERT_NO_FATAL_FAILURE(freePortBuffers(oBuffer, pm[1], allocGrap));
     return;
 }
 
@@ -713,8 +732,8 @@
         for (IOmx::ComponentInfo info : componentInfos) {
             for (std::string role : info.mRoles) {
                 if (filter.empty()) {
-                    if (kWhiteListRoles.find(role.c_str()) == kWhiteListRoles.end()) {
-                        // This is for component test and the role is not in the white list.
+                    if (kKnownRoles.find(role.c_str()) == kKnownRoles.end()) {
+                        // This is for component test and the role is not supported.
                         continue;
                     }
                 } else if (role.find(filter) == std::string::npos) {
diff --git a/media/omx/1.0/vts/functional/common/media_hidl_test_common.h b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
index 044408d..eddf83f 100644
--- a/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
+++ b/media/omx/1.0/vts/functional/common/media_hidl_test_common.h
@@ -71,8 +71,8 @@
     unknown,
 };
 
-// White list audio/video roles to be tested.
-static std::set<std::string> kWhiteListRoles{
+// List known and thus tested audio/video roles.
+static std::set<std::string> kKnownRoles{
         "audio_encoder.aac",      "audio_encoder.amrnb", "audio_encoder.amrwb",
         "audio_encoder.flac",     "audio_decoder.aac",   "audio_decoder.amrnb",
         "audio_decoder.amrwb",    "audio_decoder.flac",  "audio_decoder.g711alaw",
@@ -115,6 +115,7 @@
 struct BufferInfo {
     uint32_t id;
     bufferOwner owner;
+    buffer_handle_t handle;
     android::hardware::media::omx::V1_0::CodecBuffer omxBuffer;
     ::android::sp<IMemory> mMemory;
     int32_t slot;
@@ -329,6 +330,9 @@
                          PortMode portMode = PortMode::PRESET_BYTE_BUFFER,
                          bool allocGrap = false);
 
+void freePortBuffers(android::Vector<BufferInfo>* buffArray, PortMode portMode,
+                     bool allocGrap = false);
+
 void changeStateLoadedtoIdle(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
                              android::Vector<BufferInfo>* iBuffer,
                              android::Vector<BufferInfo>* oBuffer,
@@ -338,8 +342,9 @@
 
 void changeStateIdletoLoaded(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
                              android::Vector<BufferInfo>* iBuffer,
-                             android::Vector<BufferInfo>* oBuffer,
-                             OMX_U32 kPortIndexInput, OMX_U32 kPortIndexOutput);
+                             android::Vector<BufferInfo>* oBuffer, OMX_U32 kPortIndexInput,
+                             OMX_U32 kPortIndexOutput, PortMode* portMode = nullptr,
+                             bool allocGrap = false);
 
 void changeStateIdletoExecute(sp<IOmxNode> omxNode, sp<CodecObserver> observer);
 
diff --git a/media/omx/1.0/vts/functional/component/Android.bp b/media/omx/1.0/vts/functional/component/Android.bp
index 8fb627a..9d4d092 100644
--- a/media/omx/1.0/vts/functional/component/Android.bp
+++ b/media/omx/1.0/vts/functional/component/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalMediaOmxV1_0TargetComponentTest",
     defaults: ["VtsHalMediaOmxV1_0Defaults"],
diff --git a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
index 01cec6d..d20139f 100644
--- a/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
+++ b/media/omx/1.0/vts/functional/component/VtsHalMediaOmxV1_0TargetComponentTest.cpp
@@ -1264,6 +1264,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ComponentHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, ComponentHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/media/omx/1.0/vts/functional/master/Android.bp b/media/omx/1.0/vts/functional/master/Android.bp
deleted file mode 100644
index 5953eb5..0000000
--- a/media/omx/1.0/vts/functional/master/Android.bp
+++ /dev/null
@@ -1,25 +0,0 @@
-//
-// Copyright (C) 2017 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_test {
-    name: "VtsHalMediaOmxV1_0TargetMasterTest",
-    defaults: ["VtsHalMediaOmxV1_0Defaults"],
-    srcs: ["VtsHalMediaOmxV1_0TargetMasterTest.cpp"],
-    test_suites: [
-        "general-tests",
-        "vts",
-    ],
-}
diff --git a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp b/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
deleted file mode 100644
index 68ee900..0000000
--- a/media/omx/1.0/vts/functional/master/VtsHalMediaOmxV1_0TargetMasterTest.cpp
+++ /dev/null
@@ -1,424 +0,0 @@
-/*
- * Copyright (C) 2017 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.
- */
-
-#define LOG_TAG "media_omx_hidl_master_test"
-#ifdef __LP64__
-#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
-#endif
-
-#include <android-base/logging.h>
-#include <android-base/strings.h>
-
-#include <android/hardware/media/omx/1.0/IOmx.h>
-#include <android/hardware/media/omx/1.0/IOmxNode.h>
-#include <android/hardware/media/omx/1.0/IOmxObserver.h>
-#include <android/hardware/media/omx/1.0/IOmxStore.h>
-#include <android/hardware/media/omx/1.0/types.h>
-#include <android/hidl/allocator/1.0/IAllocator.h>
-#include <android/hidl/memory/1.0/IMapper.h>
-#include <android/hidl/memory/1.0/IMemory.h>
-#include <gtest/gtest.h>
-#include <hidl/GtestPrinter.h>
-#include <hidl/ServiceManagement.h>
-#include <media/stagefright/omx/OMXUtils.h>
-
-using ::android::sp;
-using ::android::base::Join;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::media::omx::V1_0::CodecBuffer;
-using ::android::hardware::media::omx::V1_0::IOmx;
-using ::android::hardware::media::omx::V1_0::IOmxNode;
-using ::android::hardware::media::omx::V1_0::IOmxObserver;
-using ::android::hardware::media::omx::V1_0::IOmxStore;
-using ::android::hardware::media::omx::V1_0::Message;
-using ::android::hardware::media::omx::V1_0::PortMode;
-using ::android::hidl::allocator::V1_0::IAllocator;
-using ::android::hidl::memory::V1_0::IMapper;
-using ::android::hidl::memory::V1_0::IMemory;
-
-#include <getopt.h>
-#include <media_hidl_test_common.h>
-
-class MasterHidlTest : public ::testing::TestWithParam<std::string> {
-  public:
-    virtual void SetUp() override {
-        omxStore = IOmxStore::getService(GetParam());
-        ASSERT_NE(omxStore, nullptr);
-        omx = IOmx::getService(GetParam());
-        ASSERT_NE(omx, nullptr);
-    }
-
-    sp<IOmxStore> omxStore;
-    sp<IOmx> omx;
-
-   protected:
-    static void description(const std::string& description) {
-        RecordProperty("description", description);
-    }
-};
-
-struct AttributePattern {
-    const testing::internal::RE key;
-    const testing::internal::RE value;
-};
-
-void displayComponentInfo(hidl_vec<IOmx::ComponentInfo>& nodeList) {
-    for (size_t i = 0; i < nodeList.size(); i++) {
-        printf("%s | ", nodeList[i].mName.c_str());
-        for (size_t j = 0; j < ((nodeList[i]).mRoles).size(); j++) {
-            printf("%s ", nodeList[i].mRoles[j].c_str());
-        }
-        printf("\n");
-    }
-}
-
-void validateAttributes(
-        const std::map<const std::string, const testing::internal::RE>& knownPatterns,
-        const std::vector<const struct AttributePattern>& unknownPatterns,
-        hidl_vec<IOmxStore::Attribute> attributes) {
-    std::set<const std::string> attributeKeys;
-    for (const auto& attr : attributes) {
-        // Make sure there are no duplicates
-        const auto [nodeIter, inserted] = attributeKeys.insert(attr.key);
-        EXPECT_EQ(inserted, true) << "Attribute \"" << attr.key << "\" has duplicates.";
-
-        // Check the value against the corresponding regular
-        // expression.
-        const auto knownPattern = knownPatterns.find(attr.key);
-        if (knownPattern != knownPatterns.end()) {
-            EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, knownPattern->second), true)
-                    << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value << ".";
-            ;
-        } else {
-            // Failed to find exact attribute, check against
-            // possible patterns.
-            bool keyFound = false;
-            for (const auto& unknownPattern : unknownPatterns) {
-                if (testing::internal::RE::PartialMatch(attr.key, unknownPattern.key)) {
-                    keyFound = true;
-                    EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, unknownPattern.value),
-                              true)
-                            << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value
-                            << ".";
-                }
-            }
-            if (!keyFound) {
-                std::cout << "Warning, Unrecognized attribute \"" << attr.key << "\" with value \""
-                          << attr.value << "\"." << std::endl;
-            }
-        }
-    }
-}
-
-// Make sure IOmx and IOmxStore have the same set of instances.
-TEST(MasterHidlTest, instanceMatchValidation) {
-    auto omxInstances = android::hardware::getAllHalInstanceNames(IOmx::descriptor);
-    auto omxStoreInstances = android::hardware::getAllHalInstanceNames(IOmxStore::descriptor);
-    ASSERT_EQ(omxInstances.size(), omxInstances.size());
-    for (const std::string& omxInstance : omxInstances) {
-        EXPECT_TRUE(std::find(omxStoreInstances.begin(), omxStoreInstances.end(), omxInstance) !=
-                    omxStoreInstances.end());
-    }
-}
-
-// list service attributes and verify expected formats
-TEST_P(MasterHidlTest, ListServiceAttr) {
-    description("list service attributes");
-    android::hardware::media::omx::V1_0::Status status;
-    hidl_vec<IOmxStore::Attribute> attributes;
-    EXPECT_TRUE(omxStore
-                    ->listServiceAttributes([&status, &attributes](
-                        android::hardware::media::omx::V1_0::Status _s,
-                        hidl_vec<IOmxStore::Attribute> const& _nl) {
-                        status = _s;
-                        attributes = _nl;
-                    })
-                    .isOk());
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-    if (attributes.size() == 0) {
-        std::cout << "Warning, Attribute list empty" << std::endl;
-    } else {
-        /*
-         * knownPatterns is a map whose keys are the known "key" for a service
-         * attribute pair (see IOmxStore::Attribute), and whose values are the
-         * corresponding regular expressions that will have to match with the
-         * "value" of the attribute pair. If listServiceAttributes() returns an
-         * attribute that has a matching key but an unmatched value, the test
-         * will fail.
-         */
-        const std::map<const std::string, const testing::internal::RE> knownPatterns = {
-                {"max-video-encoder-input-buffers", "0|[1-9][0-9]*"},
-                {"supports-multiple-secure-codecs", "0|1"},
-                {"supports-secure-with-non-secure-codec", "0|1"},
-        };
-        /*
-         * unknownPatterns is a vector of pairs of regular expressions.
-         * For each attribute whose key is not known (i.e., does not match any
-         * of the keys in the "knownPatterns" variable defined above), that key will be
-         * tried for a match with the first element of each pair of the variable
-         * "unknownPatterns". If a match occurs, the value of that same attribute will be
-         * tried for a match with the second element of the pair. If this second
-         * match fails, the test will fail.
-         */
-        const std::vector<const struct AttributePattern> unknownPatterns = {
-                {"supports-[a-z0-9-]*", "0|1"}};
-
-        validateAttributes(knownPatterns, unknownPatterns, attributes);
-    }
-}
-
-// get node prefix
-TEST_P(MasterHidlTest, getNodePrefix) {
-    description("get node prefix");
-    hidl_string prefix;
-    omxStore->getNodePrefix(
-        [&prefix](hidl_string const& _nl) { prefix = _nl; });
-    if (prefix.empty()) std::cout << "Warning, Node Prefix empty" << std::endl;
-}
-
-// list roles and validate all RoleInfo objects
-TEST_P(MasterHidlTest, ListRoles) {
-    description("list roles");
-    hidl_vec<IOmxStore::RoleInfo> roleList;
-    omxStore->listRoles([&roleList](hidl_vec<IOmxStore::RoleInfo> const& _nl) {
-        roleList = _nl;
-    });
-    if (roleList.size() == 0) {
-        GTEST_SKIP() << "Warning, RoleInfo list empty";
-        return;
-    }
-
-    // Basic patterns for matching
-    const std::string toggle = "(0|1)";
-    const std::string string = "(.*)";
-    const std::string num = "(0|([1-9][0-9]*))";
-    const std::string size = "(" + num + "x" + num + ")";
-    const std::string ratio = "(" + num + ":" + num + ")";
-    const std::string range_num = "((" + num + "-" + num + ")|" + num + ")";
-    const std::string range_size = "((" + size + "-" + size + ")|" + size + ")";
-    const std::string range_ratio = "((" + ratio + "-" + ratio + ")|" + ratio + ")";
-    const std::string list_range_num = "(" + range_num + "(," + range_num + ")*)";
-
-    // Matching rules for node attributes with fixed keys
-    const std::map<const std::string, const testing::internal::RE> knownPatterns = {
-            {"alignment", size},
-            {"bitrate-range", range_num},
-            {"block-aspect-ratio-range", range_ratio},
-            {"block-count-range", range_num},
-            {"block-size", size},
-            {"blocks-per-second-range", range_num},
-            {"complexity-default", num},
-            {"complexity-range", range_num},
-            {"feature-adaptive-playback", toggle},
-            {"feature-bitrate-control", "(VBR|CBR|CQ)[,(VBR|CBR|CQ)]*"},
-            {"feature-can-swap-width-height", toggle},
-            {"feature-intra-refresh", toggle},
-            {"feature-partial-frame", toggle},
-            {"feature-secure-playback", toggle},
-            {"feature-tunneled-playback", toggle},
-            {"frame-rate-range", range_num},
-            {"max-channel-count", num},
-            {"max-concurrent-instances", num},
-            {"max-supported-instances", num},
-            {"pixel-aspect-ratio-range", range_ratio},
-            {"quality-default", num},
-            {"quality-range", range_num},
-            {"quality-scale", string},
-            {"sample-rate-ranges", list_range_num},
-            {"size-range", range_size},
-    };
-
-    // Strings for matching rules for node attributes with key patterns
-    const std::vector<const struct AttributePattern> unknownPatterns = {
-            {"measured-frame-rate-" + size + "-range", range_num},
-            {"feature-[a-zA-Z0-9_-]+", string},
-    };
-
-    // Matching rules for node names and owners
-    const testing::internal::RE nodeNamePattern = "[a-zA-Z0-9._-]+";
-    const testing::internal::RE nodeOwnerPattern = "[a-zA-Z0-9._-]+";
-
-    std::set<const std::string> roleKeys;
-    std::map<const std::string, std::set<const std::string>> nodeToRoles;
-    std::map<const std::string, std::set<const std::string>> ownerToNodes;
-    for (const IOmxStore::RoleInfo& role : roleList) {
-        // Make sure there are no duplicates
-        const auto [roleIter, inserted] = roleKeys.insert(role.role);
-        EXPECT_EQ(inserted, true) << "Role \"" << role.role << "\" has duplicates.";
-
-        // Make sure role name follows expected format based on type and
-        // isEncoder
-        const std::string role_name(
-                ::android::GetComponentRole(role.isEncoder, role.type.c_str()));
-        EXPECT_EQ(role_name, role.role) << "Role \"" << role.role << "\" does not match "
-                                        << (role.isEncoder ? "an encoder " : "a decoder ")
-                                        << "for mime type \"" << role.type << ".";
-
-        // Check the nodes for this role
-        std::set<const std::string> nodeKeys;
-        for (const IOmxStore::NodeInfo& node : role.nodes) {
-            // Make sure there are no duplicates
-            const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
-            EXPECT_EQ(inserted, true) << "Node \"" << node.name << "\" has duplicates.";
-
-            // Check the format of node name
-            EXPECT_EQ(testing::internal::RE::FullMatch(node.name, nodeNamePattern), true)
-                    << "Node name \"" << node.name << " is invalid.";
-            // Check the format of node owner
-            EXPECT_EQ(testing::internal::RE::FullMatch(node.owner, nodeOwnerPattern), true)
-                    << "Node owner \"" << node.owner << " is invalid.";
-
-            validateAttributes(knownPatterns, unknownPatterns, node.attributes);
-
-            ownerToNodes[node.owner].insert(node.name);
-            nodeToRoles[node.name].insert(role.role);
-        }
-    }
-
-    // Verify the information with IOmx::listNodes().
-    // IOmxStore::listRoles() and IOmx::listNodes() should give consistent
-    // information about nodes and roles.
-    for (const auto& [owner, nodes] : ownerToNodes) {
-        // Obtain the IOmx instance for each "owner"
-        const sp<IOmx> omx = omxStore->getOmx(owner);
-        EXPECT_NE(nullptr, omx);
-
-        // Invoke IOmx::listNodes()
-        android::hardware::media::omx::V1_0::Status status;
-        hidl_vec<IOmx::ComponentInfo> nodeList;
-        EXPECT_TRUE(
-                omx->listNodes([&status, &nodeList](android::hardware::media::omx::V1_0::Status _s,
-                                                    hidl_vec<IOmx::ComponentInfo> const& _nl) {
-                       status = _s;
-                       nodeList = _nl;
-                   }).isOk());
-        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-
-        // Verify that roles for each node match with the information from
-        // IOmxStore::listRoles().
-        std::set<const std::string> nodeKeys;
-        for (IOmx::ComponentInfo node : nodeList) {
-            // Make sure there are no duplicates
-            const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
-            EXPECT_EQ(inserted, true)
-                    << "IOmx::listNodes() lists duplicate nodes \"" << node.mName << "\".";
-
-            // Skip "hidden" nodes, i.e. those that are not advertised by
-            // IOmxStore::listRoles().
-            if (nodes.find(node.mName) == nodes.end()) {
-                std::cout << "Warning, IOmx::listNodes() lists unknown node \"" << node.mName
-                          << "\" for IOmx instance \"" << owner << "\"." << std::endl;
-                continue;
-            }
-
-            // All the roles advertised by IOmxStore::listRoles() for this
-            // node must be included in roleKeys.
-            std::set<const std::string> difference;
-            std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
-                                roleKeys.begin(), roleKeys.end(),
-                                std::inserter(difference, difference.begin()));
-            EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
-                                                   "instance \""
-                                                << owner
-                                                << "\" does not report some "
-                                                   "expected nodes: "
-                                                << android::base::Join(difference, ", ") << ".";
-        }
-        // Check that all nodes obtained from IOmxStore::listRoles() are
-        // supported by the their corresponding IOmx instances.
-        std::set<const std::string> difference;
-        std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
-                            std::inserter(difference, difference.begin()));
-        EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
-                                               "instance \""
-                                            << owner
-                                            << "\" does not report some "
-                                               "expected nodes: "
-                                            << android::base::Join(difference, ", ") << ".";
-    }
-
-    if (!nodeToRoles.empty()) {
-        // Check that the prefix is a sensible string.
-        hidl_string prefix;
-        omxStore->getNodePrefix([&prefix](hidl_string const& _nl) { prefix = _nl; });
-        EXPECT_EQ(testing::internal::RE::PartialMatch(prefix, nodeNamePattern), true)
-                << "\"" << prefix << "\" is not a valid prefix for node names.";
-
-        // Check that all node names have the said prefix.
-        for (const auto& node : nodeToRoles) {
-            EXPECT_NE(node.first.rfind(prefix, 0), std::string::npos)
-                    << "Node \"" << node.first << "\" does not start with prefix \"" << prefix
-                    << "\".";
-        }
-    }
-}
-
-// list components and roles.
-TEST_P(MasterHidlTest, ListNodes) {
-    description("enumerate component and roles");
-    android::hardware::media::omx::V1_0::Status status;
-    hidl_vec<IOmx::ComponentInfo> nodeList;
-    bool isPass = true;
-    EXPECT_TRUE(
-        omx->listNodes([&status, &nodeList](
-                           android::hardware::media::omx::V1_0::Status _s,
-                           hidl_vec<IOmx::ComponentInfo> const& _nl) {
-               status = _s;
-               nodeList = _nl;
-           })
-            .isOk());
-    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-    if (nodeList.size() == 0)
-        std::cout << "Warning, ComponentInfo list empty" << std::endl;
-    else {
-        // displayComponentInfo(nodeList);
-        for (size_t i = 0; i < nodeList.size(); i++) {
-            sp<CodecObserver> observer = nullptr;
-            sp<IOmxNode> omxNode = nullptr;
-            observer = new CodecObserver(nullptr);
-            ASSERT_NE(observer, nullptr);
-            EXPECT_TRUE(
-                omx->allocateNode(
-                       nodeList[i].mName, observer,
-                       [&](android::hardware::media::omx::V1_0::Status _s,
-                           sp<IOmxNode> const& _nl) {
-                           status = _s;
-                           omxNode = _nl;
-                       })
-                    .isOk());
-            ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
-            if (omxNode == nullptr) {
-                isPass = false;
-                std::cerr << "[    !OK   ] " << nodeList[i].mName.c_str()
-                          << "\n";
-            } else {
-                EXPECT_TRUE((omxNode->freeNode()).isOk());
-                omxNode = nullptr;
-                // std::cout << "[     OK   ] " << nodeList[i].mName.c_str() <<
-                // "\n";
-            }
-        }
-    }
-    EXPECT_TRUE(isPass);
-}
-
-INSTANTIATE_TEST_CASE_P(
-        PerInstance, MasterHidlTest,
-        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOmxStore::descriptor)),
-        android::hardware::PrintInstanceNameToString);
diff --git a/media/omx/1.0/vts/functional/store/Android.bp b/media/omx/1.0/vts/functional/store/Android.bp
new file mode 100644
index 0000000..b34fff1
--- /dev/null
+++ b/media/omx/1.0/vts/functional/store/Android.bp
@@ -0,0 +1,34 @@
+//
+// Copyright (C) 2017 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalMediaOmxV1_0TargetStoreTest",
+    defaults: ["VtsHalMediaOmxV1_0Defaults"],
+    srcs: ["VtsHalMediaOmxV1_0TargetStoreTest.cpp"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
new file mode 100644
index 0000000..e73196c
--- /dev/null
+++ b/media/omx/1.0/vts/functional/store/VtsHalMediaOmxV1_0TargetStoreTest.cpp
@@ -0,0 +1,425 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "media_omx_hidl_store_test"
+#ifdef __LP64__
+#define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
+#endif
+
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+#include <android/hardware/media/omx/1.0/IOmx.h>
+#include <android/hardware/media/omx/1.0/IOmxNode.h>
+#include <android/hardware/media/omx/1.0/IOmxObserver.h>
+#include <android/hardware/media/omx/1.0/IOmxStore.h>
+#include <android/hardware/media/omx/1.0/types.h>
+#include <android/hidl/allocator/1.0/IAllocator.h>
+#include <android/hidl/memory/1.0/IMapper.h>
+#include <android/hidl/memory/1.0/IMemory.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <media/stagefright/omx/OMXUtils.h>
+
+using ::android::sp;
+using ::android::base::Join;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::media::omx::V1_0::CodecBuffer;
+using ::android::hardware::media::omx::V1_0::IOmx;
+using ::android::hardware::media::omx::V1_0::IOmxNode;
+using ::android::hardware::media::omx::V1_0::IOmxObserver;
+using ::android::hardware::media::omx::V1_0::IOmxStore;
+using ::android::hardware::media::omx::V1_0::Message;
+using ::android::hardware::media::omx::V1_0::PortMode;
+using ::android::hidl::allocator::V1_0::IAllocator;
+using ::android::hidl::memory::V1_0::IMapper;
+using ::android::hidl::memory::V1_0::IMemory;
+
+#include <getopt.h>
+#include <media_hidl_test_common.h>
+
+class StoreHidlTest : public ::testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        omxStore = IOmxStore::getService(GetParam());
+        ASSERT_NE(omxStore, nullptr);
+        omx = IOmx::getService(GetParam());
+        ASSERT_NE(omx, nullptr);
+    }
+
+    sp<IOmxStore> omxStore;
+    sp<IOmx> omx;
+
+   protected:
+    static void description(const std::string& description) {
+        RecordProperty("description", description);
+    }
+};
+
+struct AttributePattern {
+    const testing::internal::RE key;
+    const testing::internal::RE value;
+};
+
+void displayComponentInfo(hidl_vec<IOmx::ComponentInfo>& nodeList) {
+    for (size_t i = 0; i < nodeList.size(); i++) {
+        printf("%s | ", nodeList[i].mName.c_str());
+        for (size_t j = 0; j < ((nodeList[i]).mRoles).size(); j++) {
+            printf("%s ", nodeList[i].mRoles[j].c_str());
+        }
+        printf("\n");
+    }
+}
+
+void validateAttributes(
+        const std::map<const std::string, const testing::internal::RE>& knownPatterns,
+        const std::vector<const struct AttributePattern>& unknownPatterns,
+        hidl_vec<IOmxStore::Attribute> attributes) {
+    std::set<const std::string> attributeKeys;
+    for (const auto& attr : attributes) {
+        // Make sure there are no duplicates
+        const auto [nodeIter, inserted] = attributeKeys.insert(attr.key);
+        EXPECT_EQ(inserted, true) << "Attribute \"" << attr.key << "\" has duplicates.";
+
+        // Check the value against the corresponding regular
+        // expression.
+        const auto knownPattern = knownPatterns.find(attr.key);
+        if (knownPattern != knownPatterns.end()) {
+            EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, knownPattern->second), true)
+                    << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value << ".";
+            ;
+        } else {
+            // Failed to find exact attribute, check against
+            // possible patterns.
+            bool keyFound = false;
+            for (const auto& unknownPattern : unknownPatterns) {
+                if (testing::internal::RE::PartialMatch(attr.key, unknownPattern.key)) {
+                    keyFound = true;
+                    EXPECT_EQ(testing::internal::RE::FullMatch(attr.value, unknownPattern.value),
+                              true)
+                            << "Attribute \"" << attr.key << "\" has invalid value \"" << attr.value
+                            << ".";
+                }
+            }
+            if (!keyFound) {
+                std::cout << "Warning, Unrecognized attribute \"" << attr.key << "\" with value \""
+                          << attr.value << "\"." << std::endl;
+            }
+        }
+    }
+}
+
+// Make sure IOmx and IOmxStore have the same set of instances.
+TEST(StoreHidlTest, instanceMatchValidation) {
+    auto omxInstances = android::hardware::getAllHalInstanceNames(IOmx::descriptor);
+    auto omxStoreInstances = android::hardware::getAllHalInstanceNames(IOmxStore::descriptor);
+    ASSERT_EQ(omxInstances.size(), omxInstances.size());
+    for (const std::string& omxInstance : omxInstances) {
+        EXPECT_TRUE(std::find(omxStoreInstances.begin(), omxStoreInstances.end(), omxInstance) !=
+                    omxStoreInstances.end());
+    }
+}
+
+// list service attributes and verify expected formats
+TEST_P(StoreHidlTest, ListServiceAttr) {
+    description("list service attributes");
+    android::hardware::media::omx::V1_0::Status status;
+    hidl_vec<IOmxStore::Attribute> attributes;
+    EXPECT_TRUE(omxStore
+                    ->listServiceAttributes([&status, &attributes](
+                        android::hardware::media::omx::V1_0::Status _s,
+                        hidl_vec<IOmxStore::Attribute> const& _nl) {
+                        status = _s;
+                        attributes = _nl;
+                    })
+                    .isOk());
+    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
+    if (attributes.size() == 0) {
+        std::cout << "Warning, Attribute list empty" << std::endl;
+    } else {
+        /*
+         * knownPatterns is a map whose keys are the known "key" for a service
+         * attribute pair (see IOmxStore::Attribute), and whose values are the
+         * corresponding regular expressions that will have to match with the
+         * "value" of the attribute pair. If listServiceAttributes() returns an
+         * attribute that has a matching key but an unmatched value, the test
+         * will fail.
+         */
+        const std::map<const std::string, const testing::internal::RE> knownPatterns = {
+                {"max-video-encoder-input-buffers", "0|[1-9][0-9]*"},
+                {"supports-multiple-secure-codecs", "0|1"},
+                {"supports-secure-with-non-secure-codec", "0|1"},
+        };
+        /*
+         * unknownPatterns is a vector of pairs of regular expressions.
+         * For each attribute whose key is not known (i.e., does not match any
+         * of the keys in the "knownPatterns" variable defined above), that key will be
+         * tried for a match with the first element of each pair of the variable
+         * "unknownPatterns". If a match occurs, the value of that same attribute will be
+         * tried for a match with the second element of the pair. If this second
+         * match fails, the test will fail.
+         */
+        const std::vector<const struct AttributePattern> unknownPatterns = {
+                {"supports-[a-z0-9-]*", "0|1"}};
+
+        validateAttributes(knownPatterns, unknownPatterns, attributes);
+    }
+}
+
+// get node prefix
+TEST_P(StoreHidlTest, getNodePrefix) {
+    description("get node prefix");
+    hidl_string prefix;
+    omxStore->getNodePrefix(
+        [&prefix](hidl_string const& _nl) { prefix = _nl; });
+    if (prefix.empty()) std::cout << "Warning, Node Prefix empty" << std::endl;
+}
+
+// list roles and validate all RoleInfo objects
+TEST_P(StoreHidlTest, ListRoles) {
+    description("list roles");
+    hidl_vec<IOmxStore::RoleInfo> roleList;
+    omxStore->listRoles([&roleList](hidl_vec<IOmxStore::RoleInfo> const& _nl) {
+        roleList = _nl;
+    });
+    if (roleList.size() == 0) {
+        GTEST_SKIP() << "Warning, RoleInfo list empty";
+        return;
+    }
+
+    // Basic patterns for matching
+    const std::string toggle = "(0|1)";
+    const std::string string = "(.*)";
+    const std::string num = "(0|([1-9][0-9]*))";
+    const std::string size = "(" + num + "x" + num + ")";
+    const std::string ratio = "(" + num + ":" + num + ")";
+    const std::string range_num = "((" + num + "-" + num + ")|" + num + ")";
+    const std::string range_size = "((" + size + "-" + size + ")|" + size + ")";
+    const std::string range_ratio = "((" + ratio + "-" + ratio + ")|" + ratio + ")";
+    const std::string list_range_num = "(" + range_num + "(," + range_num + ")*)";
+
+    // Matching rules for node attributes with fixed keys
+    const std::map<const std::string, const testing::internal::RE> knownPatterns = {
+            {"alignment", size},
+            {"bitrate-range", range_num},
+            {"block-aspect-ratio-range", range_ratio},
+            {"block-count-range", range_num},
+            {"block-size", size},
+            {"blocks-per-second-range", range_num},
+            {"complexity-default", num},
+            {"complexity-range", range_num},
+            {"feature-adaptive-playback", toggle},
+            {"feature-bitrate-control", "(VBR|CBR|CQ)[,(VBR|CBR|CQ)]*"},
+            {"feature-can-swap-width-height", toggle},
+            {"feature-intra-refresh", toggle},
+            {"feature-partial-frame", toggle},
+            {"feature-secure-playback", toggle},
+            {"feature-tunneled-playback", toggle},
+            {"frame-rate-range", range_num},
+            {"max-channel-count", num},
+            {"max-concurrent-instances", num},
+            {"max-supported-instances", num},
+            {"pixel-aspect-ratio-range", range_ratio},
+            {"quality-default", num},
+            {"quality-range", range_num},
+            {"quality-scale", string},
+            {"sample-rate-ranges", list_range_num},
+            {"size-range", range_size},
+    };
+
+    // Strings for matching rules for node attributes with key patterns
+    const std::vector<const struct AttributePattern> unknownPatterns = {
+            {"measured-frame-rate-" + size + "-range", range_num},
+            {"feature-[a-zA-Z0-9_-]+", string},
+    };
+
+    // Matching rules for node names and owners
+    const testing::internal::RE nodeNamePattern = "[a-zA-Z0-9._-]+";
+    const testing::internal::RE nodeOwnerPattern = "[a-zA-Z0-9._-]+";
+
+    std::set<const std::string> roleKeys;
+    std::map<const std::string, std::set<const std::string>> nodeToRoles;
+    std::map<const std::string, std::set<const std::string>> ownerToNodes;
+    for (const IOmxStore::RoleInfo& role : roleList) {
+        // Make sure there are no duplicates
+        const auto [roleIter, inserted] = roleKeys.insert(role.role);
+        EXPECT_EQ(inserted, true) << "Role \"" << role.role << "\" has duplicates.";
+
+        // Make sure role name follows expected format based on type and
+        // isEncoder
+        const std::string role_name(
+                ::android::GetComponentRole(role.isEncoder, role.type.c_str()));
+        EXPECT_EQ(role_name, role.role) << "Role \"" << role.role << "\" does not match "
+                                        << (role.isEncoder ? "an encoder " : "a decoder ")
+                                        << "for mime type \"" << role.type << ".";
+
+        // Check the nodes for this role
+        std::set<const std::string> nodeKeys;
+        for (const IOmxStore::NodeInfo& node : role.nodes) {
+            // Make sure there are no duplicates
+            const auto [nodeIter, inserted] = nodeKeys.insert(node.name);
+            EXPECT_EQ(inserted, true) << "Node \"" << node.name << "\" has duplicates.";
+
+            // Check the format of node name
+            EXPECT_EQ(testing::internal::RE::FullMatch(node.name, nodeNamePattern), true)
+                    << "Node name \"" << node.name << " is invalid.";
+            // Check the format of node owner
+            EXPECT_EQ(testing::internal::RE::FullMatch(node.owner, nodeOwnerPattern), true)
+                    << "Node owner \"" << node.owner << " is invalid.";
+
+            validateAttributes(knownPatterns, unknownPatterns, node.attributes);
+
+            ownerToNodes[node.owner].insert(node.name);
+            nodeToRoles[node.name].insert(role.role);
+        }
+    }
+
+    // Verify the information with IOmx::listNodes().
+    // IOmxStore::listRoles() and IOmx::listNodes() should give consistent
+    // information about nodes and roles.
+    for (const auto& [owner, nodes] : ownerToNodes) {
+        // Obtain the IOmx instance for each "owner"
+        const sp<IOmx> omx = omxStore->getOmx(owner);
+        EXPECT_NE(nullptr, omx);
+
+        // Invoke IOmx::listNodes()
+        android::hardware::media::omx::V1_0::Status status;
+        hidl_vec<IOmx::ComponentInfo> nodeList;
+        EXPECT_TRUE(
+                omx->listNodes([&status, &nodeList](android::hardware::media::omx::V1_0::Status _s,
+                                                    hidl_vec<IOmx::ComponentInfo> const& _nl) {
+                       status = _s;
+                       nodeList = _nl;
+                   }).isOk());
+        ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
+
+        // Verify that roles for each node match with the information from
+        // IOmxStore::listRoles().
+        std::set<const std::string> nodeKeys;
+        for (IOmx::ComponentInfo node : nodeList) {
+            // Make sure there are no duplicates
+            const auto [nodeIter, inserted] = nodeKeys.insert(node.mName);
+            EXPECT_EQ(inserted, true)
+                    << "IOmx::listNodes() lists duplicate nodes \"" << node.mName << "\".";
+
+            // Skip "hidden" nodes, i.e. those that are not advertised by
+            // IOmxStore::listRoles().
+            if (nodes.find(node.mName) == nodes.end()) {
+                std::cout << "Warning, IOmx::listNodes() lists unknown node \"" << node.mName
+                          << "\" for IOmx instance \"" << owner << "\"." << std::endl;
+                continue;
+            }
+
+            // All the roles advertised by IOmxStore::listRoles() for this
+            // node must be included in roleKeys.
+            std::set<const std::string> difference;
+            std::set_difference(nodeToRoles[node.mName].begin(), nodeToRoles[node.mName].end(),
+                                roleKeys.begin(), roleKeys.end(),
+                                std::inserter(difference, difference.begin()));
+            EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
+                                                   "instance \""
+                                                << owner
+                                                << "\" does not report some "
+                                                   "expected nodes: "
+                                                << android::base::Join(difference, ", ") << ".";
+        }
+        // Check that all nodes obtained from IOmxStore::listRoles() are
+        // supported by the their corresponding IOmx instances.
+        std::set<const std::string> difference;
+        std::set_difference(nodes.begin(), nodes.end(), nodeKeys.begin(), nodeKeys.end(),
+                            std::inserter(difference, difference.begin()));
+        EXPECT_EQ(difference.empty(), true) << "IOmx::listNodes() for IOmx "
+                                               "instance \""
+                                            << owner
+                                            << "\" does not report some "
+                                               "expected nodes: "
+                                            << android::base::Join(difference, ", ") << ".";
+    }
+
+    if (!nodeToRoles.empty()) {
+        // Check that the prefix is a sensible string.
+        hidl_string prefix;
+        omxStore->getNodePrefix([&prefix](hidl_string const& _nl) { prefix = _nl; });
+        EXPECT_EQ(testing::internal::RE::PartialMatch(prefix, nodeNamePattern), true)
+                << "\"" << prefix << "\" is not a valid prefix for node names.";
+
+        // Check that all node names have the said prefix.
+        for (const auto& node : nodeToRoles) {
+            EXPECT_NE(node.first.rfind(prefix, 0), std::string::npos)
+                    << "Node \"" << node.first << "\" does not start with prefix \"" << prefix
+                    << "\".";
+        }
+    }
+}
+
+// list components and roles.
+TEST_P(StoreHidlTest, ListNodes) {
+    description("enumerate component and roles");
+    android::hardware::media::omx::V1_0::Status status;
+    hidl_vec<IOmx::ComponentInfo> nodeList;
+    bool isPass = true;
+    EXPECT_TRUE(
+        omx->listNodes([&status, &nodeList](
+                           android::hardware::media::omx::V1_0::Status _s,
+                           hidl_vec<IOmx::ComponentInfo> const& _nl) {
+               status = _s;
+               nodeList = _nl;
+           })
+            .isOk());
+    ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
+    if (nodeList.size() == 0)
+        std::cout << "Warning, ComponentInfo list empty" << std::endl;
+    else {
+        // displayComponentInfo(nodeList);
+        for (size_t i = 0; i < nodeList.size(); i++) {
+            sp<CodecObserver> observer = nullptr;
+            sp<IOmxNode> omxNode = nullptr;
+            observer = new CodecObserver(nullptr);
+            ASSERT_NE(observer, nullptr);
+            EXPECT_TRUE(
+                omx->allocateNode(
+                       nodeList[i].mName, observer,
+                       [&](android::hardware::media::omx::V1_0::Status _s,
+                           sp<IOmxNode> const& _nl) {
+                           status = _s;
+                           omxNode = _nl;
+                       })
+                    .isOk());
+            ASSERT_EQ(status, android::hardware::media::omx::V1_0::Status::OK);
+            if (omxNode == nullptr) {
+                isPass = false;
+                std::cerr << "[    !OK   ] " << nodeList[i].mName.c_str()
+                          << "\n";
+            } else {
+                EXPECT_TRUE((omxNode->freeNode()).isOk());
+                omxNode = nullptr;
+                // std::cout << "[     OK   ] " << nodeList[i].mName.c_str() <<
+                // "\n";
+            }
+        }
+    }
+    EXPECT_TRUE(isPass);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(StoreHidlTest);
+INSTANTIATE_TEST_CASE_P(
+        PerInstance, StoreHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOmxStore::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/media/omx/1.0/vts/functional/video/Android.bp b/media/omx/1.0/vts/functional/video/Android.bp
index b35c26c..5454f16 100644
--- a/media/omx/1.0/vts/functional/video/Android.bp
+++ b/media/omx/1.0/vts/functional/video/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalMediaOmxV1_0TargetVideoDecTest",
     stem: "vts_hal_media_omx_v1_0_video_dec_test",
diff --git a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
index 29a32a5..d35ce65 100644
--- a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
+++ b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoDecTest.cpp
@@ -451,6 +451,7 @@
                     status,
                     android::hardware::media::omx::V1_0::Status::TIMED_OUT);
 
+                ASSERT_NO_FATAL_FAILURE(freePortBuffers(oBuffer, oPortMode, true));
                 ASSERT_NO_FATAL_FAILURE(allocatePortBuffers(
                     omxNode, oBuffer, kPortIndexOutput, oPortMode, true));
                 status = observer->dequeueMessage(&msg, DEFAULT_TIMEOUT,
@@ -853,9 +854,9 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
 // Test for adaptive playback support
@@ -1001,9 +1002,9 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
 // end of sequence test
@@ -1067,9 +1068,9 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
 // end of sequence test
@@ -1188,9 +1189,9 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
 // end of sequence test
@@ -1295,9 +1296,9 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
 // test input/output port flush
@@ -1414,11 +1415,12 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode,
+                                                    true));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VideoDecHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, VideoDecHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
index 7519f2f..f24c6d1 100644
--- a/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
+++ b/media/omx/1.0/vts/functional/video/VtsHalMediaOmxV1_0TargetVideoEncTest.cpp
@@ -287,11 +287,11 @@
 };
 
 // Mock IOmxBufferSource class. GraphicBufferSource.cpp in libstagefright/omx/
-// implements this class. Below is dummy class introduced to test if callback
+// implements this class. Below class is introduced to test if callback
 // functions are actually being called or not
-struct DummyBufferSource : public IOmxBufferSource {
+struct MockBufferSource : public IOmxBufferSource {
    public:
-    DummyBufferSource(sp<IOmxNode> node) {
+    MockBufferSource(sp<IOmxNode> node) {
         callback = 0;
         executing = false;
         omxNode = node;
@@ -309,7 +309,7 @@
     android::Vector<BufferInfo> iBuffer, oBuffer;
 };
 
-Return<void> DummyBufferSource::onOmxExecuting() {
+Return<void> MockBufferSource::onOmxExecuting() {
     executing = true;
     callback |= 0x1;
     size_t index;
@@ -330,25 +330,25 @@
     return Void();
 };
 
-Return<void> DummyBufferSource::onOmxIdle() {
+Return<void> MockBufferSource::onOmxIdle() {
     callback |= 0x2;
     executing = false;
     return Void();
 };
 
-Return<void> DummyBufferSource::onOmxLoaded() {
+Return<void> MockBufferSource::onOmxLoaded() {
     callback |= 0x4;
     return Void();
 };
 
-Return<void> DummyBufferSource::onInputBufferAdded(uint32_t buffer) {
+Return<void> MockBufferSource::onInputBufferAdded(uint32_t buffer) {
     (void)buffer;
     EXPECT_EQ(executing, false);
     callback |= 0x8;
     return Void();
 };
 
-Return<void> DummyBufferSource::onInputBufferEmptied(
+Return<void> MockBufferSource::onInputBufferEmptied(
     uint32_t buffer, const ::android::hardware::hidl_handle& fence) {
     (void)fence;
     callback |= 0x10;
@@ -1031,7 +1031,7 @@
     setupRAWPort(omxNode, kPortIndexInput, nFrameWidth, nFrameHeight, 0,
                  xFramerate, eColorFormat);
 
-    sp<DummyBufferSource> buffersource = new DummyBufferSource(omxNode);
+    sp<MockBufferSource> buffersource = new MockBufferSource(omxNode);
     ASSERT_NE(buffersource, nullptr);
     status = omxNode->setInputSurface(buffersource);
     ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
@@ -1057,9 +1057,9 @@
     ASSERT_NO_FATAL_FAILURE(changeStateExecutetoIdle(
         omxNode, observer, &buffersource->iBuffer, &buffersource->oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(
-        omxNode, observer, &buffersource->iBuffer, &buffersource->oBuffer,
-        kPortIndexInput, kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &buffersource->iBuffer,
+                                                    &buffersource->oBuffer, kPortIndexInput,
+                                                    kPortIndexOutput, portMode));
     // test for callbacks
     EXPECT_EQ(buffersource->callback, 31);
 }
@@ -1174,9 +1174,8 @@
     ASSERT_NO_FATAL_FAILURE(
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode));
 }
 
 // test raw stream encode (input is ANW buffers)
@@ -1337,9 +1336,8 @@
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     EXPECT_EQ(portDef.nBufferCountActual, listener->freeBuffers);
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode));
 
     returnval = producer->disconnect(
         NATIVE_WINDOW_API_CPU, IGraphicBufferProducer::DisconnectMode::API);
@@ -1452,15 +1450,15 @@
         changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
     EXPECT_EQ(portDef.nBufferCountActual, listener->freeBuffers);
     // set state to executing
-    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
-                                                    &oBuffer, kPortIndexInput,
-                                                    kPortIndexOutput));
+    ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer, &oBuffer,
+                                                    kPortIndexInput, kPortIndexOutput, portMode));
 
     returnval = producer->disconnect(
         NATIVE_WINDOW_API_CPU, IGraphicBufferProducer::DisconnectMode::API);
     ASSERT_EQ(returnval, 0);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VideoEncHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, VideoEncHidlTest, testing::ValuesIn(kTestParameters),
                          android::hardware::PrintInstanceTupleNameToString<>);
 
diff --git a/memtrack/1.0/Android.bp b/memtrack/1.0/Android.bp
index a50195e..bf8db3f 100644
--- a/memtrack/1.0/Android.bp
+++ b/memtrack/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.memtrack@1.0",
     root: "android.hardware",
diff --git a/memtrack/1.0/default/Android.bp b/memtrack/1.0/default/Android.bp
index 8aa33ee..addffe3 100644
--- a/memtrack/1.0/default/Android.bp
+++ b/memtrack/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.memtrack@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/memtrack/1.0/vts/functional/Android.bp b/memtrack/1.0/vts/functional/Android.bp
index 445770a..852cd15 100644
--- a/memtrack/1.0/vts/functional/Android.bp
+++ b/memtrack/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalMemtrackV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/memtrack/1.0/vts/functional/VtsHalMemtrackV1_0TargetTest.cpp b/memtrack/1.0/vts/functional/VtsHalMemtrackV1_0TargetTest.cpp
index cccedca..26edd11 100644
--- a/memtrack/1.0/vts/functional/VtsHalMemtrackV1_0TargetTest.cpp
+++ b/memtrack/1.0/vts/functional/VtsHalMemtrackV1_0TargetTest.cpp
@@ -160,6 +160,7 @@
                   static_cast<uint32_t>(MemtrackType::NUM_TYPES));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemtrackHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, MemtrackHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IMemtrack::descriptor)),
diff --git a/memtrack/aidl/Android.bp b/memtrack/aidl/Android.bp
new file mode 100644
index 0000000..29fec24
--- /dev/null
+++ b/memtrack/aidl/Android.bp
@@ -0,0 +1,42 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.memtrack",
+    vendor_available: true,
+    srcs: ["android/hardware/memtrack/*.aidl"],
+    stability: "vintf",
+    backend: {
+        cpp: {
+            enabled: false,
+        },
+        java: {
+            enabled: false,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
new file mode 100644
index 0000000..3e25c56
--- /dev/null
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/DeviceInfo.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.memtrack;
+@VintfStability
+parcelable DeviceInfo {
+  int id;
+  @utf8InCpp String name;
+}
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
new file mode 100644
index 0000000..2e2b68e
--- /dev/null
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/IMemtrack.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.memtrack;
+@VintfStability
+interface IMemtrack {
+  android.hardware.memtrack.MemtrackRecord[] getMemory(in int pid, in android.hardware.memtrack.MemtrackType type);
+  android.hardware.memtrack.DeviceInfo[] getGpuDeviceInfo();
+}
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
new file mode 100644
index 0000000..0e15ce3
--- /dev/null
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackRecord.aidl
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.memtrack;
+@VintfStability
+parcelable MemtrackRecord {
+  int flags;
+  long sizeInBytes;
+  const int FLAG_SMAPS_ACCOUNTED = 2;
+  const int FLAG_SMAPS_UNACCOUNTED = 4;
+  const int FLAG_SHARED = 8;
+  const int FLAG_SHARED_PSS = 16;
+  const int FLAG_PRIVATE = 32;
+  const int FLAG_SYSTEM = 64;
+  const int FLAG_DEDICATED = 128;
+  const int FLAG_NONSECURE = 256;
+  const int FLAG_SECURE = 512;
+}
diff --git a/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
new file mode 100644
index 0000000..b19869e
--- /dev/null
+++ b/memtrack/aidl/aidl_api/android.hardware.memtrack/current/android/hardware/memtrack/MemtrackType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.memtrack;
+@Backing(type="int") @VintfStability
+enum MemtrackType {
+  OTHER = 0,
+  GL = 1,
+  GRAPHICS = 2,
+  MULTIMEDIA = 3,
+  CAMERA = 4,
+}
diff --git a/memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl b/memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl
new file mode 100644
index 0000000..bcba544
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/DeviceInfo.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.hardware.memtrack;
+
+/*
+ * Contains the device name and the device id.
+ */
+@VintfStability
+parcelable DeviceInfo {
+    int id;
+    @utf8InCpp String name;
+}
+
diff --git a/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
new file mode 100644
index 0000000..13c3389
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/IMemtrack.aidl
@@ -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.
+ */
+
+package android.hardware.memtrack;
+
+import android.hardware.memtrack.DeviceInfo;
+import android.hardware.memtrack.MemtrackRecord;
+import android.hardware.memtrack.MemtrackType;
+
+/**
+ * The Memory Tracker HAL is designed to return information about
+ * device-specific memory usage.
+ * The primary goal is to be able to track memory that is not
+ * trackable in any other way, for example texture memory that is allocated by
+ * a process, but not mapped in to that process's address space.
+ * A secondary goal is to be able to categorize memory used by a process into
+ * GL, graphics, etc. All memory sizes must be in real memory usage,
+ * accounting for stride, bit depth, rounding up to page size, etc.
+ *
+ * The following getMemory() categories are important for memory accounting in
+ * Android frameworks (e.g. `dumpsys meminfo`) and should be reported as described
+ * below:
+ *
+ * - MemtrackType::GRAPHICS and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
+ *     This should report the PSS of all CPU-Mapped DMA-BUFs (buffers mapped into
+ *     the process address space) and all GPU-Mapped DMA-BUFs (buffers mapped into
+ *     the GPU device address space on behalf of the process), removing any overlap
+ *     between the CPU-mapped and GPU-mapped sets.
+ *
+ * - MemtrackType::GL and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
+ *     This category should report all GPU private allocations for the specified
+ *     PID that are not accounted in /proc/<pid>/smaps.
+ *
+ * - MemtrackType::OTHER and MemtrackRecord::FLAG_SMAPS_UNACCOUNTED
+ *     Any other memory not accounted for in /proc/<pid>/smaps if any, otherwise
+ *     this should return 0.
+ *
+ * SMAPS_UNACCOUNTED memory should also include memory that is mapped with
+ * VM_PFNMAP flag set. For these mappings PSS and RSS are reported as 0 in smaps.
+ * Such mappings have no backing page structs from which PSS/RSS can be calculated.
+ *
+ * Constructor for the interface should be used to perform memtrack management
+ * setup actions and must be called once before any calls to getMemory().
+ */
+@VintfStability
+interface IMemtrack {
+    /**
+     * getMemory() populates MemtrackRecord vector with the sizes of memory
+     * plus associated flags for that memory.
+     *
+     * A process collecting memory statistics will call getMemory for each
+     * combination of pid and memory type. For each memory type that it
+     * recognizes, the HAL must fill out an array of memtrack_record
+     * structures breaking down the statistics of that memory type as much as
+     * possible. For example,
+     * getMemory(<pid>, GL) might return:
+     * { { 4096,  ACCOUNTED | PRIVATE | SYSTEM },
+     *   { 40960, UNACCOUNTED | PRIVATE | SYSTEM },
+     *   { 8192,  ACCOUNTED | PRIVATE | DEDICATED },
+     *   { 8192,  UNACCOUNTED | PRIVATE | DEDICATED } }
+     * If the HAL cannot differentiate between SYSTEM and DEDICATED memory, it
+     * could return:
+     * { { 12288,  ACCOUNTED | PRIVATE },
+     *   { 49152,  UNACCOUNTED | PRIVATE } }
+     *
+     * Memory must not overlap between types. For example, a graphics buffer
+     * that has been mapped into the GPU as a surface must show up when
+     * GRAPHICS is requested and not when GL
+     * is requested.
+     *
+     * @param pid process for which memory information is requested
+     * @param type memory type that information is being requested about
+     * @return vector of MemtrackRecord containing memory information
+     */
+    MemtrackRecord[] getMemory(in int pid, in MemtrackType type);
+
+    /**
+     * getGpuDeviceInfo() populates DeviceInfo with the ID and name
+     * of each GPU device.
+     *
+     * For example, getGpuDeviceInfor, might return:
+     * { { 0, <gpu-device-name> },
+     *   { 1, <gpu-device-name> } }
+     *
+     * This information is used to identify GPU devices for GPU specific
+     * memory accounting (e.g. DMA buffer usage).
+     *
+     * The device name(s) provided in getGpuDeviceInfo() must match the
+     * device name in the corresponding device(s) sysfs entry.
+     *
+     * @return vector of DeviceInfo populated for all GPU devices.
+     */
+    DeviceInfo[] getGpuDeviceInfo();
+}
diff --git a/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl
new file mode 100644
index 0000000..dae026e
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackRecord.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.memtrack;
+
+/*
+ * Each record consists of the size of the memory used by the process and
+ * flags indicate all the MemtrackFlags that are valid for this record.
+ */
+@VintfStability
+parcelable MemtrackRecord {
+    /* Memtrack Flags */
+    const int FLAG_SMAPS_ACCOUNTED = 1 << 1;
+    const int FLAG_SMAPS_UNACCOUNTED = 1 << 2;
+    const int FLAG_SHARED = 1 << 3;
+    const int FLAG_SHARED_PSS = 1 << 4;
+    const int FLAG_PRIVATE = 1 << 5;
+    const int FLAG_SYSTEM = 1 << 6;
+    const int FLAG_DEDICATED = 1 << 7;
+    const int FLAG_NONSECURE = 1 << 8;
+    const int FLAG_SECURE = 1 << 9;
+
+    /* Bitfield indicating all flags that are valid for this record */
+    int flags;
+
+    long sizeInBytes;
+}
+
diff --git a/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
new file mode 100644
index 0000000..5db735a
--- /dev/null
+++ b/memtrack/aidl/android/hardware/memtrack/MemtrackType.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.hardware.memtrack;
+
+/**
+ * Tags which define the usage of the memory buffers.
+ */
+@VintfStability
+@Backing(type="int")
+enum MemtrackType {
+    OTHER = 0,
+    GL = 1,
+    GRAPHICS = 2,
+    MULTIMEDIA = 3,
+    CAMERA = 4,
+}
diff --git a/memtrack/aidl/default/Android.bp b/memtrack/aidl/default/Android.bp
new file mode 100644
index 0000000..7a7feea
--- /dev/null
+++ b/memtrack/aidl/default/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.memtrack-service.example",
+    relative_install_path: "hw",
+    init_rc: ["memtrack-default.rc"],
+    vintf_fragments: ["memtrack-default.xml"],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    srcs: [
+        "main.cpp",
+        "Memtrack.cpp",
+    ],
+}
diff --git a/memtrack/aidl/default/Memtrack.cpp b/memtrack/aidl/default/Memtrack.cpp
new file mode 100644
index 0000000..49a6582
--- /dev/null
+++ b/memtrack/aidl/default/Memtrack.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "Memtrack.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+ndk::ScopedAStatus Memtrack::getMemory(int pid, MemtrackType type,
+                                       std::vector<MemtrackRecord>* _aidl_return) {
+    if (pid < 0) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+    }
+    if (type != MemtrackType::OTHER && type != MemtrackType::GL && type != MemtrackType::GRAPHICS &&
+        type != MemtrackType::MULTIMEDIA && type != MemtrackType::CAMERA) {
+        return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
+    }
+    _aidl_return->clear();
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus Memtrack::getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) {
+    _aidl_return->clear();
+    DeviceInfo dev_info = {.id = 0, .name = "virtio_gpu"};
+    _aidl_return->emplace_back(dev_info);
+    return ndk::ScopedAStatus::ok();
+}
+
+}  // namespace memtrack
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/memtrack/aidl/default/Memtrack.h b/memtrack/aidl/default/Memtrack.h
new file mode 100644
index 0000000..f2ef60e
--- /dev/null
+++ b/memtrack/aidl/default/Memtrack.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/memtrack/BnMemtrack.h>
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/MemtrackRecord.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace memtrack {
+
+class Memtrack : public BnMemtrack {
+    ndk::ScopedAStatus getMemory(int pid, MemtrackType type,
+                                 std::vector<MemtrackRecord>* _aidl_return) override;
+
+    ndk::ScopedAStatus getGpuDeviceInfo(std::vector<DeviceInfo>* _aidl_return) override;
+};
+
+}  // namespace memtrack
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/memtrack/aidl/default/main.cpp b/memtrack/aidl/default/main.cpp
new file mode 100644
index 0000000..d063d2a
--- /dev/null
+++ b/memtrack/aidl/default/main.cpp
@@ -0,0 +1,36 @@
+/*
+ * 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 "Memtrack.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::memtrack::Memtrack;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<Memtrack> memtrack = ndk::SharedRefBase::make<Memtrack>();
+
+    const std::string instance = std::string() + Memtrack::descriptor + "/default";
+    binder_status_t status =
+            AServiceManager_addService(memtrack->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // Unreachable
+}
diff --git a/memtrack/aidl/default/memtrack-default.rc b/memtrack/aidl/default/memtrack-default.rc
new file mode 100644
index 0000000..1725cd0
--- /dev/null
+++ b/memtrack/aidl/default/memtrack-default.rc
@@ -0,0 +1,4 @@
+service vendor.memtrack-default /vendor/bin/hw/android.hardware.memtrack-service.example
+    class hal
+    user nobody
+    group system
diff --git a/memtrack/aidl/default/memtrack-default.xml b/memtrack/aidl/default/memtrack-default.xml
new file mode 100644
index 0000000..3e3e0f6
--- /dev/null
+++ b/memtrack/aidl/default/memtrack-default.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.memtrack</name>
+        <fqname>IMemtrack/default</fqname>
+    </hal>
+</manifest>
+
diff --git a/memtrack/aidl/vts/Android.bp b/memtrack/aidl/vts/Android.bp
new file mode 100644
index 0000000..8614b47
--- /dev/null
+++ b/memtrack/aidl/vts/Android.bp
@@ -0,0 +1,27 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalMemtrackTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalMemtrackTargetTest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libvintf",
+    ],
+    static_libs: [
+        "android.hardware.memtrack-V1-ndk_platform",
+    ],
+    test_suites: [
+        "vts",
+    ],
+}
diff --git a/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
new file mode 100644
index 0000000..8905f50
--- /dev/null
+++ b/memtrack/aidl/vts/VtsHalMemtrackTargetTest.cpp
@@ -0,0 +1,114 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/memtrack/DeviceInfo.h>
+#include <aidl/android/hardware/memtrack/IMemtrack.h>
+#include <aidl/android/hardware/memtrack/MemtrackType.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <vintf/VintfObject.h>
+
+using aidl::android::hardware::memtrack::DeviceInfo;
+using aidl::android::hardware::memtrack::IMemtrack;
+using aidl::android::hardware::memtrack::MemtrackRecord;
+using aidl::android::hardware::memtrack::MemtrackType;
+using android::vintf::KernelVersion;
+using android::vintf::RuntimeInfo;
+using android::vintf::VintfObject;
+
+class MemtrackAidlTest : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        const auto instance = GetParam();
+        ASSERT_TRUE(AServiceManager_isDeclared(instance.c_str()));
+        auto memtrackBinder = ndk::SpAIBinder(AServiceManager_waitForService(instance.c_str()));
+        memtrack_ = IMemtrack::fromBinder(memtrackBinder);
+        ASSERT_NE(memtrack_, nullptr);
+    }
+
+    std::shared_ptr<IMemtrack> memtrack_;
+};
+
+TEST_P(MemtrackAidlTest, GetMemoryInvalidPid) {
+    int pid = -1;
+
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_->getMemory(pid, type, &records);
+
+        EXPECT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
+    }
+}
+
+TEST_P(MemtrackAidlTest, GetMemoryInvalidType) {
+    int pid = 1;
+    MemtrackType type = static_cast<MemtrackType>(-1);
+    std::vector<MemtrackRecord> records;
+
+    auto status = memtrack_->getMemory(pid, type, &records);
+
+    EXPECT_EQ(status.getExceptionCode(), EX_UNSUPPORTED_OPERATION);
+}
+
+TEST_P(MemtrackAidlTest, GetMemory) {
+    int pid = 1;
+    for (MemtrackType type : ndk::enum_range<MemtrackType>()) {
+        std::vector<MemtrackRecord> records;
+
+        auto status = memtrack_->getMemory(pid, type, &records);
+
+        EXPECT_TRUE(status.isOk());
+    }
+}
+
+TEST_P(MemtrackAidlTest, GetGpuDeviceInfo) {
+    std::vector<DeviceInfo> device_info;
+
+    auto status = memtrack_->getGpuDeviceInfo(&device_info);
+
+    // Devices with < 5.4 kernels aren't required to provide an implementation of
+    // getGpuDeviceInfo(), and can return EX_UNSUPPORTED_OPERATION
+    if (status.getExceptionCode() == EX_UNSUPPORTED_OPERATION) {
+        KernelVersion min_kernel_version = KernelVersion(5, 4, 0);
+        KernelVersion kernel_version = VintfObject::GetInstance()
+                                               ->getRuntimeInfo(RuntimeInfo::FetchFlag::CPU_VERSION)
+                                               ->kernelVersion();
+        EXPECT_LT(kernel_version, min_kernel_version)
+                << "Devices with 5.4 or later kernels must implement getGpuDeviceInfo()";
+        return;
+    }
+
+    EXPECT_TRUE(status.isOk());
+    EXPECT_FALSE(device_info.empty());
+    for (auto device : device_info) {
+        EXPECT_FALSE(device.name.empty());
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemtrackAidlTest);
+INSTANTIATE_TEST_SUITE_P(PerInstance, MemtrackAidlTest,
+                         testing::ValuesIn(android::getAidlHalInstanceNames(IMemtrack::descriptor)),
+                         android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/1.0/Android.bp b/neuralnetworks/1.0/Android.bp
index 3e740c4..7bc65ff 100644
--- a/neuralnetworks/1.0/Android.bp
+++ b/neuralnetworks/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.neuralnetworks@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/neuralnetworks/1.0/types.hal b/neuralnetworks/1.0/types.hal
index 620eefb..5bfadd3 100644
--- a/neuralnetworks/1.0/types.hal
+++ b/neuralnetworks/1.0/types.hal
@@ -308,8 +308,9 @@
      * Outputs:
      * * 0: The output 4-D tensor, of shape
      *      [batches, out_height, out_width, depth_out].
-     *      For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
-     *      the following condition must be satisfied: output_scale > input_scale * filter_scale
+     *      For output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+     *      be satisfied: output_scale > input_scale * filter_scale
      */
     CONV_2D = 3,
 
diff --git a/neuralnetworks/1.0/types.t b/neuralnetworks/1.0/types.t
index d7b26aa..be1ee07 100644
--- a/neuralnetworks/1.0/types.t
+++ b/neuralnetworks/1.0/types.t
@@ -63,361 +63,25 @@
     RELU6 = 3,
 };
 
-/**
- * How an operand is used.
- */
-enum OperandLifeTime : int32_t {
-    /**
-     * The operand is internal to the model. It's created by an operation and
-     * consumed by other operations. It must be an output operand of
-     * exactly one operation.
-     */
-    TEMPORARY_VARIABLE,
+%insert OperandLifeTime
 
-    /**
-     * The operand is an input of the model. It must not be an output
-     * operand of any operation.
-     *
-     * An operand can't be both input and output of a model.
-     */
-    MODEL_INPUT,
+%insert DeviceStatus
 
-    /**
-     * The operand is an output of the model. It must be an output
-     * operand of exactly one operation.
-     *
-     * An operand can't be both input and output of a model.
-     */
-    MODEL_OUTPUT,
+%insert PerformanceInfo
 
-    /**
-     * The operand is a constant found in Model.operandValues. It must
-     * not be an output operand of any operation.
-     */
-    CONSTANT_COPY,
+%insert Capabilities
 
-    /**
-     * The operand is a constant that was specified via a Memory
-     * object. It must not be an output operand of any operation.
-     */
-    CONSTANT_REFERENCE,
+%insert DataLocation
 
-    /**
-     * The operand does not have a value. This is valid only for optional
-     * arguments of operations.
-     */
-    NO_VALUE,
-};
+%insert Operand
 
-/**
- * Status of a device.
- */
-enum DeviceStatus : int32_t {
-    AVAILABLE,
-    BUSY,
-    OFFLINE,
-    UNKNOWN,
-};
+%insert Operation
 
-/**
- * Performance information for the reference workload.
- *
- * Used by a driver to report its performance characteristics.
- */
-struct PerformanceInfo {
-    /**
-     * Ratio of the time taken by the driver to execute the
-     * workload compared to the time the CPU would take for the
-     * same workload. A lower number is better.
-     */
-    float execTime;
+%insert Model
 
-    /**
-     * Ratio of the energy used by the driver compared to what
-     * the CPU would use for doing the same workload. A lower number
-     * is better.
-     */
-    float powerUsage;
-};
+%insert RequestArgument
 
-/**
- * The capabilities of a driver.
- */
-struct Capabilities {
-    /**
-     * Driver performance when operating on float32 data.
-     */
-    PerformanceInfo float32Performance;
-
-    /**
-     * Driver performance when operating on asymmetric 8-bit quantized data.
-     */
-    PerformanceInfo quantized8Performance;
-};
-
-/**
- * Describes the location of a data object.
- */
-struct DataLocation {
-    /**
-     * The index of the memory pool where this location is found.
-     */
-    uint32_t poolIndex;
-
-    /**
-     * Offset in bytes from the start of the pool.
-     */
-    uint32_t offset;
-
-    /**
-     * The length of the data in bytes.
-     */
-    uint32_t length;
-};
-
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
-    /**
-     * Data type of the operand.
-     */
-    OperandType type;
-
-    /**
-     * Dimensions of the operand.
-     *
-     * For a scalar operand, dimensions.size() must be 0.
-     *
-     * For a tensor operand, dimensions.size() must be at least 1;
-     * however, any of the dimensions may be unspecified.
-     *
-     * A tensor operand with all dimensions specified has "fully
-     * specified" dimensions. Whenever possible (i.e., whenever the
-     * dimensions are known at model construction time), a tensor
-     * operand should have (but is not required to have) fully
-     * specified dimensions, in order to enable the best possible
-     * performance.
-     *
-     * If a tensor operand's dimensions are not fully specified, the
-     * dimensions of the operand are deduced from the operand
-     * dimensions and values of the operation for which that operand
-     * is an output.
-     *
-     * In the following situations, a tensor operand's dimensions must
-     * be fully specified:
-     *
-     *     . The operand has lifetime CONSTANT_COPY or
-     *       CONSTANT_REFERENCE.
-     *
-     *     . The operand has lifetime MODEL_INPUT or MODEL_OUTPUT. Fully
-     *       specified dimensions must either be present in the
-     *       Operand or they must be provided in the corresponding
-     *       RequestArgument.
-     *       EXCEPTION: If the input or output is optional and omitted
-     *       (by setting the hasNoValue field of the corresponding
-     *       RequestArgument to true) then it need not have fully
-     *       specified dimensions.
-     *
-     * A tensor operand with some number of unspecified dimensions is
-     * represented by setting each unspecified dimension to 0.
-     */
-    vec<uint32_t> dimensions;
-
-    /**
-     * The number of times this operand appears as an operation input.
-     *
-     * (For example, if this operand appears once in one operation's
-     * input list, and three times in another operation's input list,
-     * then numberOfConsumers = 4.)
-     */
-    uint32_t numberOfConsumers;
-
-    /**
-     * Quantized scale of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
-     * TENSOR_INT32.
-     */
-    float scale;
-
-    /**
-     * Quantized zero-point offset of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
-     */
-    int32_t zeroPoint;
-
-    /**
-     * How the operand is used.
-     */
-    OperandLifeTime lifetime;
-
-    /**
-     * Where to find the data for this operand.
-     * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
-     * NO_VALUE:
-     * - All the fields must be 0.
-     * If the lifetime is CONSTANT_COPY:
-     * - location.poolIndex is 0.
-     * - location.offset is the offset in bytes into Model.operandValues.
-     * - location.length is set.
-     * If the lifetime is CONSTANT_REFERENCE:
-     * - location.poolIndex is set.
-     * - location.offset is the offset in bytes into the specified pool.
-     * - location.length is set.
-     */
-    DataLocation location;
-};
-
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
-    /**
-     * The operation type.
-     */
-    OperationType type;
-
-    /**
-     * Describes the table that contains the indexes of the inputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> inputs;
-
-    /**
-     * Describes the table that contains the indexes of the outputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> outputs;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * might not be known is the shape of the input tensors.
- */
-struct Model {
-    /**
-     * All operands included in the model.
-     */
-    vec<Operand> operands;
-
-    /**
-     * All operations included in the model.
-     *
-     * The operations are sorted into execution order. Every operand
-     * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
-     * written before it is read.
-     */
-    vec<Operation> operations;
-
-    /**
-     * Input indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> inputIndexes;
-
-    /**
-     * Output indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> outputIndexes;
-
-    /**
-     * A byte buffer containing operand data that were copied into the model.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_COPY.
-     */
-    vec<uint8_t> operandValues;
-
-    /**
-     * A collection of shared memory pools containing operand values.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_REFERENCE.
-     */
-    vec<memory> pools;
-};
-
-/**
- * Metadata information specifying the location of the input or output data and
- * any updates to the input or output operand.
- */
-struct RequestArgument {
-    /**
-     * If true, the argument does not have a value. This can be used for
-     * operations that take optional arguments. If true, the fields of location
-     * are set to 0 and the dimensions vector is left empty.
-     */
-    bool hasNoValue;
-
-    /**
-     * The location within one of the memory pools passed in the Request.
-     */
-    DataLocation location;
-
-    /**
-     * Updated dimension information.
-     *
-     * If dimensions.size() > 0, dimension information was provided
-     * along with the argument. This can be the case for models that
-     * accept inputs of varying size. This can't change the rank, just
-     * the value of the dimensions that were unspecified in the
-     * model. If dimensions.size() > 0, then all dimensions must be
-     * specified here; and any dimension that was specified in the
-     * model must have the same value here.
-     *
-     * If the dimensions in the model are not fully specified, then
-     * they must be fully specified here, unless hasNoValue is set to
-     * true. If the dimensions in the model are fully specified, then
-     * either dimensions.size() may be 0, or the dimensions in the
-     * model must be identical to the dimensions here.
-     */
-    vec<uint32_t> dimensions;
-};
-
-/**
- * Inputs to be sent to and outputs to be retrieved from a prepared model.
- *
- * A Request serves two primary tasks:
- * 1) Provides the input and output data to be used when executing the model.
- * 2) Specifies any updates to the input operand metadata that were left
- *    unspecified at model preparation time.
- *
- * An output must not overlap with any other output, with an input, or
- * with an operand of lifetime CONSTANT_REFERENCE.
- */
-struct Request {
-    /**
-     * Input data and information to be used in the execution of a prepared
-     * model.
-     *
-     * The index of the input corresponds to the index in Model.inputIndexes.
-     *   E.g., input[i] corresponds to Model.inputIndexes[i].
-     */
-    vec<RequestArgument> inputs;
-
-    /**
-     * Output data and information to be used in the execution of a prepared
-     * model.
-     *
-     * The index of the output corresponds to the index in Model.outputIndexes.
-     *   E.g., output[i] corresponds to Model.outputIndexes[i].
-     */
-    vec<RequestArgument> outputs;
-
-    /**
-     * A collection of shared memory pools containing operand data for both the
-     * inputs and the outputs to a model.
-     */
-    vec<memory> pools;
-};
+%insert Request
 
 /**
  * Return status of a function.
diff --git a/neuralnetworks/1.0/utils/Android.bp b/neuralnetworks/1.0/utils/Android.bp
new file mode 100644
index 0000000..0ad9926
--- /dev/null
+++ b/neuralnetworks/1.0/utils/Android.bp
@@ -0,0 +1,69 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_1_0",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal/1.0/"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+    ],
+    export_static_lib_headers: [
+        "neuralnetworks_utils_hal_common",
+    ],
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_1_0_test",
+    srcs: ["test/*.cpp"],
+    static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.0/utils/OWNERS b/neuralnetworks/1.0/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/1.0/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
new file mode 100644
index 0000000..8bd2fbe
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Burst.h
@@ -0,0 +1,61 @@
+/*
+ * 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_1_0_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_BURST_H
+
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+// 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::V1_0::utils {
+
+// Class that adapts nn::IPreparedModel to nn::IBurst.
+class Burst final : public nn::IBurst {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Burst>> create(
+            nn::SharedPreparedModel preparedModel);
+
+    Burst(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel);
+
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+  private:
+    const nn::SharedPreparedModel kPreparedModel;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_BURST_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
new file mode 100644
index 0000000..3b32e1d
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Callbacks.h
@@ -0,0 +1,87 @@
+/*
+ * 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_1_0_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+// 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::V1_0::utils {
+
+// Converts the results of IDevice::getSupportedOperations* to the NN canonical format. On success,
+// this function returns with the supported operations as indicated by a driver. On failure, this
+// function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+        ErrorStatus status, const hidl_vec<bool>& supportedOperations);
+
+// Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
+// function returns with a non-null nn::SharedPreparedModel with a feature level of
+// nn::Version::ANDROID_OC_MR1. On failure, this function returns with the appropriate
+// nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with an empty output shape vector and no timing information. On failure, this function
+// returns with the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        ErrorStatus status);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel asynchronously.
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+// A HIDL callback class to receive the results of IDevice::execute asynchronously.
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(ErrorStatus status) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
new file mode 100644
index 0000000..5d4bdbc
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Conversions.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_CONVERSIONS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_CONVERSIONS_H
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+namespace android::nn {
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime);
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus);
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const hal::V1_0::PerformanceInfo& performanceInfo);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities);
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation);
+GeneralResult<Model::OperandValues> unvalidatedConvert(
+        const hardware::hidl_vec<uint8_t>& operandValues);
+GeneralResult<SharedMemory> unvalidatedConvert(const hardware::hidl_memory& memory);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model);
+GeneralResult<Request::Argument> unvalidatedConvert(
+        const hal::V1_0::RequestArgument& requestArgument);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status);
+
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus);
+GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_0::Model& model);
+GeneralResult<Request> convert(const hal::V1_0::Request& request);
+GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status);
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime);
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status);
+
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status);
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_CONVERSIONS_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
new file mode 100644
index 0000000..db3b2ad
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Device.h
@@ -0,0 +1,91 @@
+/*
+ * 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_1_0_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_0::utils {
+
+// Class that adapts V1_0::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_0::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, nn::Capabilities capabilities,
+           sp<V1_0::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    const std::string kName;
+    const std::string kVersionString = "UNKNOWN";
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const sp<V1_0::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
new file mode 100644
index 0000000..e201e25
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Execution.h
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_0::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
new file mode 100644
index 0000000..48be595
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/PreparedModel.h
@@ -0,0 +1,78 @@
+/*
+ * 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_1_0_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_0::utils {
+
+// Class that adapts V1_0::IPreparedModel to nn::IPreparedModel.
+class PreparedModel final : public nn::IPreparedModel,
+                            public std::enable_shared_from_this<PreparedModel> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_0::IPreparedModel> preparedModel);
+
+    PreparedModel(PrivateConstructorTag tag, sp<V1_0::IPreparedModel> preparedModel,
+                  hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const V1_0::Request& request, const hal::utils::RequestRelocation& relocation) const;
+
+  private:
+    const sp<V1_0::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h
new file mode 100644
index 0000000..11fbb9e
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Service.h
@@ -0,0 +1,31 @@
+/*
+ * 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_1_0_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
new file mode 100644
index 0000000..1baabdf
--- /dev/null
+++ b/neuralnetworks/1.0/utils/include/nnapi/hal/1.0/Utils.h
@@ -0,0 +1,69 @@
+/*
+ * 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_1_0_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_H
+
+#include "nnapi/hal/1.0/Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/HandleError.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+constexpr auto kVersion = nn::Version::ANDROID_OC_MR1;
+
+template <typename Type>
+nn::Result<void> validate(const Type& halObject) {
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
+    }
+    return {};
+}
+
+template <typename Type>
+bool valid(const Type& halObject) {
+    const auto result = utils::validate(halObject);
+    if (!result.has_value()) {
+        LOG(ERROR) << result.error();
+    }
+    return result.has_value();
+}
+
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+        -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+    return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_H
diff --git a/neuralnetworks/1.0/utils/src/Assertions.cpp b/neuralnetworks/1.0/utils/src/Assertions.cpp
new file mode 100644
index 0000000..0f00951
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Assertions.cpp
@@ -0,0 +1,122 @@
+/*
+ * 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/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Types.h>
+#include <type_traits>
+
+namespace {
+
+#define COMPARE_ENUMS_TYPES(lhsType, rhsType)                                                   \
+    static_assert(                                                                              \
+            std::is_same_v<                                                                     \
+                    std::underlying_type_t<::android::hardware::neuralnetworks::V1_0::lhsType>, \
+                    std::underlying_type_t<::android::nn::rhsType>>,                            \
+            "::android::hardware::neuralnetworks::V1_0::" #lhsType                              \
+            " does not have the same underlying type as ::android::nn::" #rhsType)
+
+COMPARE_ENUMS_TYPES(OperandType, OperandType);
+COMPARE_ENUMS_TYPES(OperationType, OperationType);
+COMPARE_ENUMS_TYPES(ErrorStatus, ErrorStatus);
+COMPARE_ENUMS_TYPES(OperandLifeTime, Operand::LifeTime);
+
+#undef COMPARE_ENUMS_TYPES
+
+#define COMPARE_ENUMS_FULL(lhsSymbol, rhsSymbol, lhsType, rhsType)                               \
+    static_assert(                                                                               \
+            static_cast<                                                                         \
+                    std::underlying_type_t<::android::hardware::neuralnetworks::V1_0::lhsType>>( \
+                    ::android::hardware::neuralnetworks::V1_0::lhsType::lhsSymbol) ==            \
+                    static_cast<std::underlying_type_t<::android::nn::rhsType>>(                 \
+                            ::android::nn::rhsType::rhsSymbol),                                  \
+            "::android::hardware::neuralnetworks::V1_0::" #lhsType "::" #lhsSymbol               \
+            " does not match ::android::nn::" #rhsType "::" #rhsSymbol)
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, OperandType, OperandType)
+
+COMPARE_ENUMS(FLOAT32);
+COMPARE_ENUMS(INT32);
+COMPARE_ENUMS(UINT32);
+COMPARE_ENUMS(TENSOR_FLOAT32);
+COMPARE_ENUMS(TENSOR_INT32);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM);
+COMPARE_ENUMS(OEM);
+COMPARE_ENUMS(TENSOR_OEM_BYTE);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, OperationType, OperationType)
+
+COMPARE_ENUMS(ADD);
+COMPARE_ENUMS(AVERAGE_POOL_2D);
+COMPARE_ENUMS(CONCATENATION);
+COMPARE_ENUMS(CONV_2D);
+COMPARE_ENUMS(DEPTHWISE_CONV_2D);
+COMPARE_ENUMS(DEPTH_TO_SPACE);
+COMPARE_ENUMS(DEQUANTIZE);
+COMPARE_ENUMS(EMBEDDING_LOOKUP);
+COMPARE_ENUMS(FLOOR);
+COMPARE_ENUMS(FULLY_CONNECTED);
+COMPARE_ENUMS(HASHTABLE_LOOKUP);
+COMPARE_ENUMS(L2_NORMALIZATION);
+COMPARE_ENUMS(L2_POOL_2D);
+COMPARE_ENUMS(LOCAL_RESPONSE_NORMALIZATION);
+COMPARE_ENUMS(LOGISTIC);
+COMPARE_ENUMS(LSH_PROJECTION);
+COMPARE_ENUMS(LSTM);
+COMPARE_ENUMS(MAX_POOL_2D);
+COMPARE_ENUMS(MUL);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+COMPARE_ENUMS(RESHAPE);
+COMPARE_ENUMS(RESIZE_BILINEAR);
+COMPARE_ENUMS(RNN);
+COMPARE_ENUMS(SOFTMAX);
+COMPARE_ENUMS(SPACE_TO_DEPTH);
+COMPARE_ENUMS(SVDF);
+COMPARE_ENUMS(TANH);
+COMPARE_ENUMS(OEM_OPERATION);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, ErrorStatus, ErrorStatus)
+
+COMPARE_ENUMS(NONE);
+COMPARE_ENUMS(DEVICE_UNAVAILABLE);
+COMPARE_ENUMS(GENERAL_FAILURE);
+COMPARE_ENUMS(OUTPUT_INSUFFICIENT_SIZE);
+COMPARE_ENUMS(INVALID_ARGUMENT);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(lhsSymbol, rhsSymbol) \
+    COMPARE_ENUMS_FULL(lhsSymbol, rhsSymbol, OperandLifeTime, Operand::LifeTime)
+
+COMPARE_ENUMS(TEMPORARY_VARIABLE, TEMPORARY_VARIABLE);
+COMPARE_ENUMS(MODEL_INPUT, SUBGRAPH_INPUT);
+COMPARE_ENUMS(MODEL_OUTPUT, SUBGRAPH_OUTPUT);
+COMPARE_ENUMS(CONSTANT_COPY, CONSTANT_COPY);
+COMPARE_ENUMS(CONSTANT_REFERENCE, CONSTANT_REFERENCE);
+COMPARE_ENUMS(NO_VALUE, NO_VALUE);
+
+#undef COMPARE_ENUMS
+
+#undef COMPARE_ENUMS_FULL
+
+}  // anonymous namespace
diff --git a/neuralnetworks/1.0/utils/src/Burst.cpp b/neuralnetworks/1.0/utils/src/Burst.cpp
new file mode 100644
index 0000000..1284721
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Burst.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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 "Burst.h"
+
+#include <android-base/logging.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
+        nn::SharedPreparedModel preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "V1_0::utils::Burst::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(preparedModel));
+}
+
+Burst::Burst(PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel)
+    : kPreparedModel(std::move(preparedModel)) {
+    CHECK(kPreparedModel != nullptr);
+}
+
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& /*memory*/) const {
+    return nullptr;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+}
+
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Callbacks.cpp b/neuralnetworks/1.0/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..ea3ea56
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Callbacks.cpp
@@ -0,0 +1,88 @@
+/*
+ * 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 "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+// 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::V1_0::utils {
+
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+        ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+    HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+    return supportedOperations;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    return NN_TRY(PreparedModel::create(preparedModel));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        ErrorStatus status) {
+    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    return {};
+}
+
+Return<void> PreparedModelCallback::notify(ErrorStatus status,
+                                           const sp<IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(ErrorStatus status) {
+    mData.put(executionCallback(status));
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Conversions.cpp b/neuralnetworks/1.0/utils/src/Conversions.cpp
new file mode 100644
index 0000000..c0498eb
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Conversions.cpp
@@ -0,0 +1,429 @@
+/*
+ * 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 "Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+#include <variant>
+
+#include "Utils.h"
+
+namespace {
+
+template <typename Type>
+constexpr std::underlying_type_t<Type> underlyingType(Type value) {
+    return static_cast<std::underlying_type_t<Type>>(value);
+}
+
+}  // namespace
+
+namespace android::nn {
+namespace {
+
+using hardware::hidl_memory;
+using hardware::hidl_vec;
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    NN_TRY(hal::V1_0::utils::compliantVersion(canonical));
+    return canonical;
+}
+
+}  // anonymous namespace
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_0::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_0::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+GeneralResult<Operand::LifeTime> unvalidatedConvert(const hal::V1_0::OperandLifeTime& lifetime) {
+    return static_cast<Operand::LifeTime>(lifetime);
+}
+
+GeneralResult<DeviceStatus> unvalidatedConvert(const hal::V1_0::DeviceStatus& deviceStatus) {
+    return static_cast<DeviceStatus>(deviceStatus);
+}
+
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const hal::V1_0::PerformanceInfo& performanceInfo) {
+    return Capabilities::PerformanceInfo{
+            .execTime = performanceInfo.execTime,
+            .powerUsage = performanceInfo.powerUsage,
+    };
+}
+
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_0::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
+
+    auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
+                                                                      quantized8Performance);
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = float32Performance,
+            .relaxedFloat32toFloat16PerformanceTensor = float32Performance,
+            .operandPerformance = std::move(table),
+    };
+}
+
+GeneralResult<DataLocation> unvalidatedConvert(const hal::V1_0::DataLocation& location) {
+    return DataLocation{
+            .poolIndex = location.poolIndex,
+            .offset = location.offset,
+            .length = location.length,
+    };
+}
+
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_0::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+    };
+}
+
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_0::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+GeneralResult<Model::OperandValues> unvalidatedConvert(const hidl_vec<uint8_t>& operandValues) {
+    return Model::OperandValues(operandValues.data(), operandValues.size());
+}
+
+GeneralResult<SharedMemory> unvalidatedConvert(const hidl_memory& memory) {
+    return hal::utils::createSharedMemoryFromHidlMemory(memory);
+}
+
+GeneralResult<Model> unvalidatedConvert(const hal::V1_0::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
+
+    // Verify number of consumers.
+    const auto numberOfConsumers =
+            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]) {
+            return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
+        }
+    }
+
+    auto main = Model::Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
+            .operations = std::move(operations),
+            .inputIndexes = model.inputIndexes,
+            .outputIndexes = model.outputIndexes,
+    };
+
+    return Model{
+            .main = std::move(main),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+    };
+}
+
+GeneralResult<Request::Argument> unvalidatedConvert(const hal::V1_0::RequestArgument& argument) {
+    const auto lifetime = argument.hasNoValue ? Request::Argument::LifeTime::NO_VALUE
+                                              : Request::Argument::LifeTime::POOL;
+    return Request::Argument{
+            .lifetime = lifetime,
+            .location = NN_TRY(unvalidatedConvert(argument.location)),
+            .dimensions = argument.dimensions,
+    };
+}
+
+GeneralResult<Request> unvalidatedConvert(const hal::V1_0::Request& request) {
+    auto memories = NN_TRY(unvalidatedConvert(request.pools));
+    std::vector<Request::MemoryPool> pools;
+    pools.reserve(memories.size());
+    std::move(memories.begin(), memories.end(), std::back_inserter(pools));
+
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = std::move(pools),
+    };
+}
+
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_0::ErrorStatus& status) {
+    switch (status) {
+        case hal::V1_0::ErrorStatus::NONE:
+        case hal::V1_0::ErrorStatus::DEVICE_UNAVAILABLE:
+        case hal::V1_0::ErrorStatus::GENERAL_FAILURE:
+        case hal::V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case hal::V1_0::ErrorStatus::INVALID_ARGUMENT:
+            return static_cast<ErrorStatus>(status);
+    }
+    return NN_ERROR(ErrorStatus::GENERAL_FAILURE)
+           << "Invalid ErrorStatus " << underlyingType(status);
+}
+
+GeneralResult<DeviceStatus> convert(const hal::V1_0::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_0::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_0::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<Request> convert(const hal::V1_0::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_0::ErrorStatus& status) {
+    return validatedConvert(status);
+}
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(utils::unvalidatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+template <typename Type>
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return utils::unvalidatedConvert(canonical);
+}
+
+}  // anonymous namespace
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
+    if (lifetime == nn::Operand::LifeTime::POINTER) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+    return static_cast<OperandLifeTime>(lifetime);
+}
+
+nn::GeneralResult<DeviceStatus> unvalidatedConvert(const nn::DeviceStatus& deviceStatus) {
+    return static_cast<DeviceStatus>(deviceStatus);
+}
+
+nn::GeneralResult<PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo) {
+    return PerformanceInfo{
+            .execTime = performanceInfo.execTime,
+            .powerUsage = performanceInfo.powerUsage,
+    };
+}
+
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
+    return Capabilities{
+            .float32Performance = NN_TRY(unvalidatedConvert(
+                    capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
+                    capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
+    };
+}
+
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+    return DataLocation{
+            .poolIndex = location.poolIndex,
+            .offset = location.offset,
+            .length = location.length,
+    };
+}
+
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .numberOfConsumers = 0,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+    };
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return hidl_vec<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
+}
+
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    return hal::utils::createHidlMemoryFromSharedMemory(memory);
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+    if (!hal::utils::hasNoPointerData(model)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
+
+    // Update number of consumers.
+    const auto numberOfConsumers =
+            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];
+    }
+
+    return Model{
+            .operands = std::move(operands),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
+            .inputIndexes = model.main.inputIndexes,
+            .outputIndexes = model.main.outputIndexes,
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+    };
+}
+
+nn::GeneralResult<RequestArgument> unvalidatedConvert(
+        const nn::Request::Argument& requestArgument) {
+    if (requestArgument.lifetime == nn::Request::Argument::LifeTime::POINTER) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+    const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE;
+    return RequestArgument{
+            .hasNoValue = hasNoValue,
+            .location = NN_TRY(unvalidatedConvert(requestArgument.location)),
+            .dimensions = requestArgument.dimensions,
+    };
+}
+
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
+    return unvalidatedConvert(std::get<nn::SharedMemory>(memoryPool));
+}
+
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
+    if (!hal::utils::hasNoPointerData(request)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
+    };
+}
+
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& status) {
+    switch (status) {
+        case nn::ErrorStatus::NONE:
+        case nn::ErrorStatus::DEVICE_UNAVAILABLE:
+        case nn::ErrorStatus::GENERAL_FAILURE:
+        case nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case nn::ErrorStatus::INVALID_ARGUMENT:
+            return static_cast<ErrorStatus>(status);
+        default:
+            return ErrorStatus::GENERAL_FAILURE;
+    }
+}
+
+nn::GeneralResult<DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return validatedConvert(deviceStatus);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& status) {
+    return validatedConvert(status);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Device.cpp b/neuralnetworks/1.0/utils/src/Device.cpp
new file mode 100644
index 0000000..93bd81a
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Device.cpp
@@ -0,0 +1,180 @@
+/*
+ * 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 "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_0::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
+                                                         const Capabilities& capabilities) {
+    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    return nn::convert(capabilities);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_0::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(capabilitiesCallback);
+
+    const auto ret = device->getCapabilities(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_0::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_0::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_0::utils::Device::create must have non-null device";
+    }
+
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
+                                          std::move(capabilities), std::move(device),
+                                          std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, nn::Capabilities capabilities,
+               sp<V1_0::IDevice> device, hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kCapabilities(std::move(capabilities)),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_OC_MR1;
+}
+
+nn::DeviceType Device::getType() const {
+    return nn::DeviceType::OTHER;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    auto cb = hal::utils::CallbackValue(supportedOperationsCallback);
+
+    const auto ret = kDevice->getSupportedOperations(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference /*preference*/, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel(hidlModel, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::prepareModelFromCache not supported on 1.0 HAL service";
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.0 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Execution.cpp b/neuralnetworks/1.0/utils/src/Execution.cpp
new file mode 100644
index 0000000..7a3216b
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Execution.cpp
@@ -0,0 +1,72 @@
+/*
+ * 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.
+ */
+
+#include "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_0::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kPreparedModel->executeInternal(kRequest, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on 1.0 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/PreparedModel.cpp b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..00970c0
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/PreparedModel.cpp
@@ -0,0 +1,131 @@
+/*
+ * 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 "Burst.h"
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Execution.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_0::utils {
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_0::IPreparedModel> preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_0::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel),
+                                                 std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, sp<V1_0::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kPreparedModel(std::move(preparedModel)), kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming /*measure*/,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+
+    return executeInternal(hidlRequest, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kPreparedModel->execute(request, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+
+    auto result = NN_TRY(cb->get());
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return result;
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& /*request*/,
+                             const std::vector<nn::SyncFence>& /*waitFor*/,
+                             nn::MeasureTiming /*measure*/,
+                             const nn::OptionalTimePoint& /*deadline*/,
+                             const nn::OptionalDuration& /*loopTimeoutDuration*/,
+                             const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IPreparedModel::executeFenced is not supported on 1.0 HAL service";
+}
+
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation));
+}
+
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+    return Burst::create(shared_from_this());
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_0::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/src/Service.cpp b/neuralnetworks/1.0/utils/src/Service.cpp
new file mode 100644
index 0000000..ec28b1d
--- /dev/null
+++ b/neuralnetworks/1.0/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/test/DeviceTest.cpp b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..e881da2
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/DeviceTest.cpp
@@ -0,0 +1,524 @@
+/*
+ * 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 "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const nn::Model kSimpleModel = {
+        .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+                              {.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+                 .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+                 .inputIndexes = {0},
+                 .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const sp<V1_0::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+        .execTime = std::numeric_limits<float>::max(),
+        .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+    return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+        std::apply(cb, argPack);
+        return Void();
+    };
+}
+
+sp<MockDevice> createMockDevice() {
+    const auto mockDevice = MockDevice::create();
+
+    // Setup default actions for each relevant call.
+    const auto getCapabilities_ret = makeCallbackReturn(
+            V1_0::ErrorStatus::NONE, V1_0::Capabilities{
+                                             .float32Performance = kNoPerformanceInfo,
+                                             .quantized8Performance = kNoPerformanceInfo,
+                                     });
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getCapabilities(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+                             const sp<V1_0::utils::MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](const V1_0::Model& /*model*/,
+                                                       const sp<V1_0::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_0::ErrorStatus> {
+        cb->notify(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(DeviceTest, invalidName) {
+    // run test
+    const auto device = MockDevice::create();
+    const auto result = Device::create(kInvalidName, device);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+    // run test
+    const auto result = Device::create(kName, kInvalidDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                        V1_0::Capabilities{
+                                                .float32Performance = kNoPerformanceInfo,
+                                                .quantized8Performance = kNoPerformanceInfo,
+                                        });
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, linkToDeathError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto& name = device->getName();
+
+    // verify result
+    EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify result
+    EXPECT_EQ(featureLevel, nn::Version::ANDROID_OC_MR1);
+}
+
+TEST(DeviceTest, getCachedData) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto result = Device::create(kName, mockDevice);
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& device = result.value();
+
+    // run test and verify results
+    EXPECT_EQ(device->getVersionString(), device->getVersionString());
+    EXPECT_EQ(device->getType(), device->getType());
+    EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+    EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+    EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, wait) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<void> { return {}; };
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& model, const auto& cb) {
+        cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& supportedOperations = result.value();
+    EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+    EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& /*model*/, const auto& cb) {
+        cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = V1_0::utils::MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNotSupported) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateNotSupported) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/utils/test/MockDevice.h b/neuralnetworks/1.0/utils/test/MockDevice.h
new file mode 100644
index 0000000..7c399ec
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/MockDevice.h
@@ -0,0 +1,86 @@
+/*
+ * 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_1_0_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockDevice final : public IDevice {
+  public:
+    static sp<MockDevice> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations,
+                (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+                (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+    auto mockDevice = sp<MockDevice>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                            uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+    // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+    // to determine which object is dead. However, the utils::Device code only pairs a single death
+    // recipient with a single HIDL interface object, so these arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.0/utils/test/MockPreparedModel.h b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..03f1a4b
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/MockPreparedModel.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+  public:
+    static sp<MockPreparedModel> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+                             uint64_t /*cookie*/) override;
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+                (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+                (override));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+    auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                                   uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+    // in 0 and nullptr for these arguments instead. Normally, they are used by the
+    // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+    // code only pairs a single death recipient with a single HIDL interface object, so these
+    // arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_0_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..7820c06
--- /dev/null
+++ b/neuralnetworks/1.0/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,400 @@
+/*
+ * 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 "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_0::IPreparedModel> kInvalidPreparedModel;
+
+sp<MockPreparedModel> createMockPreparedModel() {
+    return MockPreparedModel::create();
+}
+
+auto makeExecute(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus) {
+    return [launchStatus, returnStatus](
+                   const V1_0::Request& /*request*/,
+                   const sp<V1_0::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> {
+        cb->notify(returnStatus);
+        return launchStatus;
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+    // run test
+    const auto result = PreparedModel::create(kInvalidPreparedModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, execute) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                         V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecute) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecute(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                         V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecute(V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurst) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto resource = preparedModel->getUnderlyingResource();
+
+    // verify resource
+    const sp<V1_0::IPreparedModel>* maybeMock = std::any_cast<sp<V1_0::IPreparedModel>>(&resource);
+    ASSERT_NE(maybeMock, nullptr);
+    EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
diff --git a/neuralnetworks/1.0/vts/functional/Android.bp b/neuralnetworks/1.0/vts/functional/Android.bp
index d802911..9a91560 100644
--- a/neuralnetworks/1.0/vts/functional/Android.bp
+++ b/neuralnetworks/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "neuralnetworks_vts_functional_defaults",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/neuralnetworks/1.0/vts/functional/AndroidTest.xml b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
index 13671f9..9dd85ae 100644
--- a/neuralnetworks/1.0/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.0/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_0TargetTest" />
+        <option name="native-test-timeout" value="20m" />
     </test>
 </configuration>
diff --git a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
index 1a55c2f..66fefb1 100644
--- a/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.0/vts/functional/GeneratedTestHarness.h
@@ -43,6 +43,7 @@
 std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
 
 #define INSTANTIATE_GENERATED_TEST(TestSuite, filter)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                             \
     INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite,                                    \
                              testing::Combine(testing::ValuesIn(getNamedDevices()),       \
                                               testing::ValuesIn(getNamedModels(filter))), \
diff --git a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
index 17f4613..d2cd20c 100644
--- a/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.0/vts/functional/VtsHalNeuralnetworks.h
@@ -42,6 +42,7 @@
         const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
 
 #define INSTANTIATE_DEVICE_TEST(TestSuite)                                                 \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                              \
     INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
                              printNeuralnetworksHidlTest)
 
diff --git a/neuralnetworks/1.1/Android.bp b/neuralnetworks/1.1/Android.bp
index bef21c0..772e5e6 100644
--- a/neuralnetworks/1.1/Android.bp
+++ b/neuralnetworks/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.neuralnetworks@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDevice.hal",
diff --git a/neuralnetworks/1.1/types.t b/neuralnetworks/1.1/types.t
index 75ac2e7..8c22b30 100644
--- a/neuralnetworks/1.1/types.t
+++ b/neuralnetworks/1.1/types.t
@@ -31,128 +31,10 @@
 %insert Operation_1.1
 };
 
-/**
- * The capabilities of a driver.
- */
-struct Capabilities {
-    /**
-     * Driver performance when operating on float32 data.
-     */
-    PerformanceInfo float32Performance;
+%insert Capabilities
 
-    /**
-     * Driver performance when operating on asymmetric 8-bit quantized data.
-     */
-    PerformanceInfo quantized8Performance;
+%insert Operation
 
-    /**
-     * Driver performance when operating on float32 data but performing
-     * calculations with range and/or precision as low as that of the IEEE
-     * 754 16-bit floating-point format.
-     */
-    PerformanceInfo relaxedFloat32toFloat16Performance;
-};
+%insert Model
 
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
-    /**
-     * The operation type.
-     */
-    OperationType type;
-
-    /**
-     * Describes the table that contains the indexes of the inputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> inputs;
-
-    /**
-     * Describes the table that contains the indexes of the outputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> outputs;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
-    /**
-     * All operands included in the model.
-     */
-    vec<Operand> operands;
-
-    /**
-     * All operations included in the model.
-     *
-     * The operations are sorted into execution order. Every operand
-     * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
-     * written before it is read.
-     */
-    vec<Operation> operations;
-
-    /**
-     * Input indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> inputIndexes;
-
-    /**
-     * Output indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> outputIndexes;
-
-    /**
-     * A byte buffer containing operand data that were copied into the model.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_COPY.
-     */
-    vec<uint8_t> operandValues;
-
-    /**
-     * A collection of shared memory pools containing operand values.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_REFERENCE.
-     */
-    vec<memory> pools;
-
-    /**
-     * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
-     * precision as low as that of the IEEE 754 16-bit floating-point format.
-     * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
-     * range and precision of the IEEE 754 32-bit floating-point format.
-     */
-    bool relaxComputationFloat32toFloat16;
-};
-
-/**
- * Execution preferences.
- */
-enum ExecutionPreference : int32_t {
-    /**
-     * Prefer executing in a way that minimizes battery drain.
-     * This is desirable for compilations that will be executed often.
-     */
-    LOW_POWER = 0,
-    /**
-     * Prefer returning a single answer as fast as possible, even if this causes
-     * more power consumption.
-     */
-    FAST_SINGLE_ANSWER = 1,
-    /**
-     * Prefer maximizing the throughput of successive frames, for example when
-     * processing successive frames coming from the camera.
-     */
-    SUSTAINED_SPEED = 2,
-};
+%insert ExecutionPreference
diff --git a/neuralnetworks/1.1/utils/Android.bp b/neuralnetworks/1.1/utils/Android.bp
new file mode 100644
index 0000000..d9e82d4
--- /dev/null
+++ b/neuralnetworks/1.1/utils/Android.bp
@@ -0,0 +1,73 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_1_1",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal/1.1/"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+    ],
+    export_static_lib_headers: [
+        "neuralnetworks_utils_hal_common",
+    ],
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_1_1_test",
+    srcs: ["test/*.cpp"],
+    static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.1/utils/OWNERS b/neuralnetworks/1.1/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/1.1/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
new file mode 100644
index 0000000..5d0769f
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Conversions.h
@@ -0,0 +1,60 @@
+/*
+ * 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_1_1_CONVERSIONS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_CONVERSIONS_H
+
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+namespace android::nn {
+
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model);
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const hal::V1_1::ExecutionPreference& executionPreference);
+
+GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_1::Model& model);
+GeneralResult<ExecutionPreference> convert(
+        const hal::V1_1::ExecutionPreference& executionPreference);
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference);
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request);
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status);
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_CONVERSIONS_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
new file mode 100644
index 0000000..5e224b5
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Device.h
@@ -0,0 +1,91 @@
+/*
+ * 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_1_1_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_1::utils {
+
+// Class that adapts V1_1::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_1::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, nn::Capabilities capabilities,
+           sp<V1_1::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    const std::string kName;
+    const std::string kVersionString = "UNKNOWN";
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const sp<V1_1::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h
new file mode 100644
index 0000000..a3ad3cf
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Service.h
@@ -0,0 +1,31 @@
+/*
+ * 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_1_1_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
new file mode 100644
index 0000000..a8cf8cf
--- /dev/null
+++ b/neuralnetworks/1.1/utils/include/nnapi/hal/1.1/Utils.h
@@ -0,0 +1,71 @@
+/*
+ * 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_1_1_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_H
+
+#include "nnapi/hal/1.1/Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+constexpr auto kDefaultExecutionPreference = ExecutionPreference::FAST_SINGLE_ANSWER;
+constexpr auto kVersion = nn::Version::ANDROID_P;
+
+template <typename Type>
+nn::Result<void> validate(const Type& halObject) {
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
+    }
+    return {};
+}
+
+template <typename Type>
+bool valid(const Type& halObject) {
+    const auto result = utils::validate(halObject);
+    if (!result.has_value()) {
+        LOG(ERROR) << result.error();
+    }
+    return result.has_value();
+}
+
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+        -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+    return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_H
diff --git a/neuralnetworks/1.1/utils/src/Assertions.cpp b/neuralnetworks/1.1/utils/src/Assertions.cpp
new file mode 100644
index 0000000..ba4a388
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Assertions.cpp
@@ -0,0 +1,100 @@
+/*
+ * 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/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Types.h>
+#include <type_traits>
+
+namespace {
+
+#define COMPARE_ENUMS_TYPES(type)                                                                  \
+    static_assert(std::is_same_v<                                                                  \
+                          std::underlying_type_t<::android::hardware::neuralnetworks::V1_1::type>, \
+                          std::underlying_type_t<::android::nn::type>>,                            \
+                  "::android::hardware::neuralnetworks::V1_1::" #type                              \
+                  " does not have the same underlying type as ::android::nn::" #type)
+
+COMPARE_ENUMS_TYPES(OperationType);
+COMPARE_ENUMS_TYPES(ExecutionPreference);
+
+#undef COMPARE_ENUMS_TYPES
+
+#define COMPARE_ENUMS_FULL(symbol, type)                                                          \
+    static_assert(                                                                                \
+            static_cast<std::underlying_type_t<::android::hardware::neuralnetworks::V1_1::type>>( \
+                    ::android::hardware::neuralnetworks::V1_1::type::symbol) ==                   \
+                    static_cast<std::underlying_type_t<::android::nn::type>>(                     \
+                            ::android::nn::type::symbol),                                         \
+            "::android::hardware::neuralnetworks::V1_1::" #type "::" #symbol                      \
+            " does not match ::android::nn::" #type "::" #symbol)
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperationType)
+
+COMPARE_ENUMS(ADD);
+COMPARE_ENUMS(AVERAGE_POOL_2D);
+COMPARE_ENUMS(CONCATENATION);
+COMPARE_ENUMS(CONV_2D);
+COMPARE_ENUMS(DEPTHWISE_CONV_2D);
+COMPARE_ENUMS(DEPTH_TO_SPACE);
+COMPARE_ENUMS(DEQUANTIZE);
+COMPARE_ENUMS(EMBEDDING_LOOKUP);
+COMPARE_ENUMS(FLOOR);
+COMPARE_ENUMS(FULLY_CONNECTED);
+COMPARE_ENUMS(HASHTABLE_LOOKUP);
+COMPARE_ENUMS(L2_NORMALIZATION);
+COMPARE_ENUMS(L2_POOL_2D);
+COMPARE_ENUMS(LOCAL_RESPONSE_NORMALIZATION);
+COMPARE_ENUMS(LOGISTIC);
+COMPARE_ENUMS(LSH_PROJECTION);
+COMPARE_ENUMS(LSTM);
+COMPARE_ENUMS(MAX_POOL_2D);
+COMPARE_ENUMS(MUL);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+COMPARE_ENUMS(RESHAPE);
+COMPARE_ENUMS(RESIZE_BILINEAR);
+COMPARE_ENUMS(RNN);
+COMPARE_ENUMS(SOFTMAX);
+COMPARE_ENUMS(SPACE_TO_DEPTH);
+COMPARE_ENUMS(SVDF);
+COMPARE_ENUMS(TANH);
+COMPARE_ENUMS(BATCH_TO_SPACE_ND);
+COMPARE_ENUMS(DIV);
+COMPARE_ENUMS(MEAN);
+COMPARE_ENUMS(PAD);
+COMPARE_ENUMS(SPACE_TO_BATCH_ND);
+COMPARE_ENUMS(SQUEEZE);
+COMPARE_ENUMS(STRIDED_SLICE);
+COMPARE_ENUMS(SUB);
+COMPARE_ENUMS(TRANSPOSE);
+COMPARE_ENUMS(OEM_OPERATION);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, ExecutionPreference)
+
+COMPARE_ENUMS(LOW_POWER);
+COMPARE_ENUMS(FAST_SINGLE_ANSWER);
+COMPARE_ENUMS(SUSTAINED_SPEED);
+
+#undef COMPARE_ENUMS
+
+#undef COMPARE_ENUMS_FULL
+
+}  // anonymous namespace
diff --git a/neuralnetworks/1.1/utils/src/Conversions.cpp b/neuralnetworks/1.1/utils/src/Conversions.cpp
new file mode 100644
index 0000000..467ceb3
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Conversions.cpp
@@ -0,0 +1,272 @@
+/*
+ * 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 "Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <type_traits>
+#include <utility>
+
+#include "Utils.h"
+
+namespace android::nn {
+namespace {
+
+using hardware::hidl_vec;
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    NN_TRY(hal::V1_1::utils::compliantVersion(canonical));
+    return canonical;
+}
+
+}  // anonymous namespace
+
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_1::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_1::Capabilities& capabilities) {
+    const auto quantized8Performance =
+            NN_TRY(unvalidatedConvert(capabilities.quantized8Performance));
+    const auto float32Performance = NN_TRY(unvalidatedConvert(capabilities.float32Performance));
+    const auto relaxedFloat32toFloat16Performance =
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16Performance));
+
+    auto table = hal::utils::makeQuantized8PerformanceConsistentWithP(float32Performance,
+                                                                      quantized8Performance);
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16Performance,
+            .relaxedFloat32toFloat16PerformanceTensor = relaxedFloat32toFloat16Performance,
+            .operandPerformance = std::move(table),
+    };
+}
+
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_1::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+GeneralResult<Model> unvalidatedConvert(const hal::V1_1::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
+
+    // Verify number of consumers.
+    const auto numberOfConsumers =
+            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]) {
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
+        }
+    }
+
+    auto main = Model::Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
+            .operations = std::move(operations),
+            .inputIndexes = model.inputIndexes,
+            .outputIndexes = model.outputIndexes,
+    };
+
+    return Model{
+            .main = std::move(main),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+    };
+}
+
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const hal::V1_1::ExecutionPreference& executionPreference) {
+    return static_cast<ExecutionPreference>(executionPreference);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_1::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_1::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<ExecutionPreference> convert(
+        const hal::V1_1::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+namespace {
+
+using utils::unvalidatedConvert;
+
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo) {
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
+}
+
+nn::GeneralResult<V1_0::Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return V1_0::utils::unvalidatedConvert(operand);
+}
+
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
+}
+
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
+}
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+template <typename Type>
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return unvalidatedConvert(canonical);
+}
+
+}  // anonymous namespace
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
+    return Capabilities{
+            .float32Performance = NN_TRY(unvalidatedConvert(
+                    capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_FLOAT32))),
+            .quantized8Performance = NN_TRY(unvalidatedConvert(
+                    capabilities.operandPerformance.lookup(nn::OperandType::TENSOR_QUANT8_ASYMM))),
+            .relaxedFloat32toFloat16Performance = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+    };
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+    if (!hal::utils::hasNoPointerData(model)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Mdoel cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
+
+    // Update number of consumers.
+    const auto numberOfConsumers =
+            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];
+    }
+
+    return Model{
+            .operands = std::move(operands),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
+            .inputIndexes = model.main.inputIndexes,
+            .outputIndexes = model.main.outputIndexes,
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+    };
+}
+
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference) {
+    return static_cast<ExecutionPreference>(executionPreference);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return V1_0::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request) {
+    return V1_0::utils::convert(request);
+}
+
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status) {
+    return V1_0::utils::convert(status);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Device.cpp b/neuralnetworks/1.1/utils/src/Device.cpp
new file mode 100644
index 0000000..3197ef4
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Device.cpp
@@ -0,0 +1,181 @@
+/*
+ * 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 "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_1::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
+                                                         const Capabilities& capabilities) {
+    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    return nn::convert(capabilities);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_1::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(capabilitiesCallback);
+
+    const auto ret = device->getCapabilities_1_1(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_1::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_1::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_1::utils::Device::create must have non-null device";
+    }
+
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(PrivateConstructorTag{}, std::move(name),
+                                          std::move(capabilities), std::move(device),
+                                          std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, nn::Capabilities capabilities,
+               sp<V1_1::IDevice> device, hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kCapabilities(std::move(capabilities)),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_P;
+}
+
+nn::DeviceType Device::getType() const {
+    return nn::DeviceType::UNKNOWN;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return std::make_pair(/*numModelCache=*/0, /*numDataCache=*/0);
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    auto cb = hal::utils::CallbackValue(V1_0::utils::supportedOperationsCallback);
+
+    const auto ret = kDevice->getSupportedOperations_1_1(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(convert(preference));
+
+    const auto cb = sp<V1_0::utils::PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel_1_1(hidlModel, hidlPreference, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::prepareModelFromCache not supported on 1.1 HAL service";
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.1 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/src/Service.cpp b/neuralnetworks/1.1/utils/src/Service.cpp
new file mode 100644
index 0000000..e2d3240
--- /dev/null
+++ b/neuralnetworks/1.1/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/test/DeviceTest.cpp b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..41e0e30
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/DeviceTest.cpp
@@ -0,0 +1,534 @@
+/*
+ * 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 "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const nn::Model kSimpleModel = {
+        .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+                              {.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+                 .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+                 .inputIndexes = {0},
+                 .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const sp<V1_1::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+        .execTime = std::numeric_limits<float>::max(),
+        .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+    return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+        std::apply(cb, argPack);
+        return Void();
+    };
+}
+
+sp<MockDevice> createMockDevice() {
+    const auto mockDevice = MockDevice::create();
+
+    // Setup default actions for each relevant call.
+    const auto getCapabilities_ret =
+            makeCallbackReturn(V1_0::ErrorStatus::NONE,
+                               V1_1::Capabilities{
+                                       .float32Performance = kNoPerformanceInfo,
+                                       .quantized8Performance = kNoPerformanceInfo,
+                                       .relaxedFloat32toFloat16Performance = kNoPerformanceInfo,
+                               });
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getCapabilities_1_1(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+    // Ensure that older calls are not used.
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+                             const sp<V1_0::utils::MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](const V1_1::Model& /*model*/,
+                                                       V1_1::ExecutionPreference /*preference*/,
+                                                       const sp<V1_0::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_0::ErrorStatus> {
+        cb->notify(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(DeviceTest, invalidName) {
+    // run test
+    const auto device = MockDevice::create();
+    const auto result = Device::create(kInvalidName, device);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+    // run test
+    const auto result = Device::create(kName, kInvalidDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret =
+            makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                               V1_1::Capabilities{
+                                       .float32Performance = kNoPerformanceInfo,
+                                       .quantized8Performance = kNoPerformanceInfo,
+                                       .relaxedFloat32toFloat16Performance = kNoPerformanceInfo,
+                               });
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, linkToDeathError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto& name = device->getName();
+
+    // verify result
+    EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify result
+    EXPECT_EQ(featureLevel, nn::Version::ANDROID_P);
+}
+
+TEST(DeviceTest, getCachedData) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto result = Device::create(kName, mockDevice);
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& device = result.value();
+
+    // run test and verify results
+    EXPECT_EQ(device->getVersionString(), device->getVersionString());
+    EXPECT_EQ(device->getType(), device->getType());
+    EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+    EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+    EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, wait) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<void> { return {}; };
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& model, const auto& cb) {
+        cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& supportedOperations = result.value();
+    EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+    EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& /*model*/, const auto& cb) {
+        cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = V1_0::utils::MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNotSupported) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateNotSupported) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
diff --git a/neuralnetworks/1.1/utils/test/MockDevice.h b/neuralnetworks/1.1/utils/test/MockDevice.h
new file mode 100644
index 0000000..db7392d
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/MockDevice.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_1::utils {
+
+class MockDevice final : public IDevice {
+  public:
+    static sp<MockDevice> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations,
+                (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+                (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+    // V1_1 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+                (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+                (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+                 const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+    auto mockDevice = sp<MockDevice>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                            uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+    // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+    // to determine which object is dead. However, the utils::Device code only pairs a single death
+    // recipient with a single HIDL interface object, so these arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_1::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.1/utils/test/MockPreparedModel.h b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..257397d
--- /dev/null
+++ b/neuralnetworks/1.1/utils/test/MockPreparedModel.h
@@ -0,0 +1,44 @@
+/*
+ * 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_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.0/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_0::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+  public:
+    static sp<MockPreparedModel> create();
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+                (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+                (override));
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+    return sp<MockPreparedModel>::make();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_0::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_1_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.1/vts/functional/Android.bp b/neuralnetworks/1.1/vts/functional/Android.bp
index 405548f..826ba96 100644
--- a/neuralnetworks/1.1/vts/functional/Android.bp
+++ b/neuralnetworks/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalNeuralnetworksV1_1TargetTest",
     defaults: ["neuralnetworks_vts_functional_defaults"],
diff --git a/neuralnetworks/1.1/vts/functional/AndroidTest.xml b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
index cfde60c..74001f9 100644
--- a/neuralnetworks/1.1/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.1/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_1TargetTest" />
+        <option name="native-test-timeout" value="20m" />
     </test>
 </configuration>
diff --git a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
index 4b1a96e..7a4a03b 100644
--- a/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.1/vts/functional/GeneratedTestHarness.h
@@ -43,6 +43,7 @@
 std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
 
 #define INSTANTIATE_GENERATED_TEST(TestSuite, filter)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                             \
     INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite,                                    \
                              testing::Combine(testing::ValuesIn(getNamedDevices()),       \
                                               testing::ValuesIn(getNamedModels(filter))), \
diff --git a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
index e879d84..208a930 100644
--- a/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.1/vts/functional/VtsHalNeuralnetworks.h
@@ -41,6 +41,7 @@
         const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
 
 #define INSTANTIATE_DEVICE_TEST(TestSuite)                                                 \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                              \
     INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
                              printNeuralnetworksHidlTest)
 
diff --git a/neuralnetworks/1.2/Android.bp b/neuralnetworks/1.2/Android.bp
index 4aa90aa..2b83d39 100644
--- a/neuralnetworks/1.2/Android.bp
+++ b/neuralnetworks/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.neuralnetworks@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBurstCallback.hal",
diff --git a/neuralnetworks/1.2/IDevice.hal b/neuralnetworks/1.2/IDevice.hal
index ff20c12..5fb339f 100644
--- a/neuralnetworks/1.2/IDevice.hal
+++ b/neuralnetworks/1.2/IDevice.hal
@@ -40,7 +40,7 @@
      * NNAPI applications filter devices based on their needs:
      *     - An application demands a certain level of performance, but a specific version of
      *       the driver cannot meet that requirement because of a performance regression.
-     *       The application can blacklist the driver based on the version provided.
+     *       The application can disallow the driver based on the version provided.
      *     - An application has a minimum precision requirement, but certain versions of
      *       the driver cannot meet that requirement because of bugs or certain optimizations.
      *       The application can filter out versions of these drivers.
diff --git a/neuralnetworks/1.2/types.hal b/neuralnetworks/1.2/types.hal
index 92cf2aa..f5b6ead 100644
--- a/neuralnetworks/1.2/types.hal
+++ b/neuralnetworks/1.2/types.hal
@@ -314,7 +314,8 @@
      *      tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
      *      Since HAL version 1.2, for a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
      *      the scale and zeroPoint values can be different from
-     *      input tensors. Before HAL version 1.2 they have to be the same as for the input tensors.
+     *      input tensors. Before HAL version 1.2 they have to be the same as for the
+     *      input tensors.
      */
     CONCATENATION = @1.1::OperationType:CONCATENATION,
 
@@ -460,8 +461,9 @@
      * Outputs:
      * * 0: The output 4-D tensor, of shape
      *      [batches, out_height, out_width, depth_out].
-     *      Before HAL version 1.2, for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
-     *      the following condition must be satisfied: output_scale > input_scale * filter_scale
+     *      Before HAL version 1.2, for output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+     *      be satisfied: output_scale > input_scale * filter_scale
      */
     CONV_2D = @1.1::OperationType:CONV_2D,
 
@@ -3616,7 +3618,7 @@
      *      front of dimension i.
      *      padding[i, 1] specifies the number of elements to be padded after
      *      the end of dimension i.
-     * * 2: An scalar specifying the value to use for padding input0.
+     * * 2: A scalar specifying the value to use for padding input0.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
      *      pad value must be of {@link OperandType::FLOAT16}.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
@@ -4709,14 +4711,14 @@
     // HAL equivalent of unknown type and a 1.2 HAL implementation must belong
     // to one of the categories below.
     /** The device does not fall into any category below. */
-    OTHER             = 1,
+    OTHER = 1,
     /** The device runs NNAPI models on single or multi-core CPU. */
-    CPU               = 2,
+    CPU = 2,
     /** The device can run NNAPI models and also accelerate graphics APIs such
-      * as OpenGL ES and Vulkan. */
-    GPU               = 3,
+     * as OpenGL ES and Vulkan. */
+    GPU = 3,
     /** Dedicated accelerator for Machine Learning workloads. */
-    ACCELERATOR       = 4,
+    ACCELERATOR = 4,
 };
 
 /**
@@ -4853,15 +4855,18 @@
     /**
      * Quantized scale of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
-     * TENSOR_INT32.
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
      */
     float scale;
 
     /**
      * Quantized zero-point offset of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
      */
     int32_t zeroPoint;
 
@@ -4890,25 +4895,25 @@
      * Additional parameters specific to a particular operand type.
      */
     safe_union ExtraParams {
-       /**
-        * No additional parameters.
-        */
-       Monostate none;
+        /**
+         * No additional parameters.
+         */
+        Monostate none;
 
-       /**
-        * Symmetric per-channel quantization parameters.
-        *
-        * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
-        */
-       SymmPerChannelQuantParams channelQuant;
+        /**
+         * Symmetric per-channel quantization parameters.
+         *
+         * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
+         */
+        SymmPerChannelQuantParams channelQuant;
 
-       /**
-        * Extension operand parameters.
-        *
-        * The framework treats this as an opaque data blob.
-        * The format is up to individual extensions.
-        */
-       vec<uint8_t> extension;
+        /**
+         * Extension operand parameters.
+         *
+         * The framework treats this as an opaque data blob.
+         * The format is up to individual extensions.
+         */
+        vec<uint8_t> extension;
     } extraParams;
 };
 
@@ -5049,7 +5054,7 @@
  * Specifies whether or not to measure timing information during execution.
  */
 enum MeasureTiming : int32_t {
-    NO  = 0,
+    NO = 0,
     YES = 1,
 };
 
diff --git a/neuralnetworks/1.2/types.t b/neuralnetworks/1.2/types.t
index d197f6b..b490f7f 100644
--- a/neuralnetworks/1.2/types.t
+++ b/neuralnetworks/1.2/types.t
@@ -97,376 +97,23 @@
     BASE_MAX        = 0xFFFF,
 };
 
-/**
- * Device types.
- *
- * The type of NNAPI device.
- */
-enum DeviceType : int32_t {
-    // Leaving 0 unused as it means unknown type in NDK NNAPI. There is no
-    // HAL equivalent of unknown type and a 1.2 HAL implementation must belong
-    // to one of the categories below.
-    /** The device does not fall into any category below. */
-    OTHER             = 1,
-    /** The device runs NNAPI models on single or multi-core CPU. */
-    CPU               = 2,
-    /** The device can run NNAPI models and also accelerate graphics APIs such
-      * as OpenGL ES and Vulkan. */
-    GPU               = 3,
-    /** Dedicated accelerator for Machine Learning workloads. */
-    ACCELERATOR       = 4,
-};
+%insert DeviceType
 
-/**
- * The capabilities of a driver.
- *
- * Performance of an operation comes from the type of its first operand.
- * This represents performance for non extension operand types.
- */
-struct Capabilities {
-    /**
-     * Driver performance when operating on float32 data but performing
-     * calculations with range and/or precision as low as that of the IEEE
-     * 754 16-bit floating-point format.
-     */
-    PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
-    PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+%insert Capabilities
 
-    /**
-     * Driver performance when operating on a particular data type.
-     * In the case of float32 data, this is used when the calculations
-     * are not relaxed.
-     */
-    struct OperandPerformance {
-        OperandType type;
-        PerformanceInfo info;
-    };
+%insert Operation
 
-    /**
-     * Performance by operand type. Must be sorted by OperandType.
-     * If a particular OperandType is not present in operandPerformance,
-     * its performance is treated as { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
-     */
-    vec<OperandPerformance> operandPerformance;
-};
+%insert SymmPerChannelQuantParams
 
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
-    /**
-     * The operation type.
-     *
-     * Besides the values listed in {@link OperationType}, any value above
-     * {@link OperationTypeRange::BASE_MAX} is possible and should be interpreted
-     * as an extension type according to {@link Model::extensionNameToPrefix}.
-     */
-    OperationType type;
+%insert Operand
 
-    /**
-     * Describes the table that contains the indexes of the inputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> inputs;
+%insert Model
 
-    /**
-     * Describes the table that contains the indexes of the outputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> outputs;
-};
+%insert OutputShape
 
-/**
- * Parameters for TENSOR_QUANT8_SYMM_PER_CHANNEL operand.
- */
-struct SymmPerChannelQuantParams {
-    /** Array of scaling values for each channel. Each value must be greater than zero. */
-    vec<float> scales;
-    /** Index of the channel dimension */
-    uint32_t channelDim;
-};
+%insert MeasureTiming
 
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
-    /**
-     * The data type.
-     *
-     * Besides the values listed in {@link OperandType}, any value above
-     * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
-     * as an extension type according to {@link Model::extensionNameToPrefix}.
-     */
-    OperandType type;
-
-    /**
-     * Dimensions of the operand.
-     *
-     * For a scalar operand, dimensions.size() must be 0.
-     *
-     * A tensor operand with all dimensions specified has "fully
-     * specified" dimensions. Whenever possible (i.e., whenever the
-     * dimensions are known at model construction time), a tensor
-     * operand should have (but is not required to have) fully
-     * specified dimensions, in order to enable the best possible
-     * performance.
-     *
-     * If a tensor operand's dimensions are not fully specified, the
-     * dimensions of the operand are deduced from the operand
-     * dimensions and values of the operation for which that operand
-     * is an output.
-     *
-     * In the following situations, a tensor operand's dimensions must
-     * be fully specified:
-     *
-     *     . The operand has lifetime CONSTANT_COPY or
-     *       CONSTANT_REFERENCE.
-     *
-     *     . The operand has lifetime MODEL_INPUT. Fully
-     *       specified dimensions must either be present in the
-     *       Operand or they must be provided in the corresponding
-     *       RequestArgument.
-     *       EXCEPTION: If the input is optional and omitted
-     *       (by setting the hasNoValue field of the corresponding
-     *       RequestArgument to true) then it need not have fully
-     *       specified dimensions.
-     *
-     * A tensor operand with some number of unspecified dimensions is
-     * represented by setting each unspecified dimension to 0.
-     *
-     * A tensor operand with unspecified rank is represented by providing
-     * an empty dimensions vector.
-     */
-    vec<uint32_t> dimensions;
-
-    /**
-     * The number of times this operand appears as an operation input.
-     *
-     * (For example, if this operand appears once in one operation's
-     * input list, and three times in another operation's input list,
-     * then numberOfConsumers = 4.)
-     */
-    uint32_t numberOfConsumers;
-
-    /**
-     * Quantized scale of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
-     * TENSOR_INT32.
-     */
-    float scale;
-
-    /**
-     * Quantized zero-point offset of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
-     */
-    int32_t zeroPoint;
-
-    /**
-     * How the operand is used.
-     */
-    OperandLifeTime lifetime;
-
-    /**
-     * Where to find the data for this operand.
-     * If the lifetime is TEMPORARY_VARIABLE, MODEL_INPUT, MODEL_OUTPUT, or
-     * NO_VALUE:
-     * - All the fields must be 0.
-     * If the lifetime is CONSTANT_COPY:
-     * - location.poolIndex is 0.
-     * - location.offset is the offset in bytes into Model.operandValues.
-     * - location.length is set.
-     * If the lifetime is CONSTANT_REFERENCE:
-     * - location.poolIndex is set.
-     * - location.offset is the offset in bytes into the specified pool.
-     * - location.length is set.
-     */
-    DataLocation location;
-
-    /**
-     * Additional parameters specific to a particular operand type.
-     */
-    safe_union ExtraParams {
-       /**
-        * No additional parameters.
-        */
-       Monostate none;
-
-       /**
-        * Symmetric per-channel quantization parameters.
-        *
-        * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
-        */
-       SymmPerChannelQuantParams channelQuant;
-
-       /**
-        * Extension operand parameters.
-        *
-        * The framework treats this as an opaque data blob.
-        * The format is up to individual extensions.
-        */
-       vec<uint8_t> extension;
-    } extraParams;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
-    /**
-     * All operands included in the model.
-     */
-    vec<Operand> operands;
-
-    /**
-     * All operations included in the model.
-     *
-     * The operations are sorted into execution order. Every operand
-     * with lifetime MODEL_OUTPUT or TEMPORARY_VARIABLE must be
-     * written before it is read.
-     */
-    vec<Operation> operations;
-
-    /**
-     * Input indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> inputIndexes;
-
-    /**
-     * Output indexes of the model. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> outputIndexes;
-
-    /**
-     * A byte buffer containing operand data that were copied into the model.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_COPY.
-     */
-    vec<uint8_t> operandValues;
-
-    /**
-     * A collection of shared memory pools containing operand values.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_REFERENCE.
-     */
-    vec<memory> pools;
-
-    /**
-     * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
-     * precision as low as that of the IEEE 754 16-bit floating-point format.
-     * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
-     * range and precision of the IEEE 754 32-bit floating-point format.
-     */
-    bool relaxComputationFloat32toFloat16;
-
-    /**
-     * The mapping between extension names and prefixes of operand and
-     * operation type values.
-     *
-     * An operand or operation whose numeric type value is above
-     * {@link OperandTypeRange::BASE_MAX} or
-     * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
-     * as an extension operand. The low
-     * {@link Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the value
-     * correspond to the type ID within the extension and the high
-     * {@link Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
-     * the "prefix", which maps uniquely to the extension name.
-     *
-     * For example, if a model contains an operation whose value is
-     * 0xAAAABBBB and extensionNameToPrefix contains an entry with
-     * prefix=0xAAAA and name="vendor.test.test_extension", then
-     * the operation should be interpreted as the operation 0xBBBB
-     * of the extension named vendor.test.test_extension.
-     *
-     * This is a one-to-one correspondence. That is, there must be at most one
-     * prefix corresponding to each extension name and at most one extension
-     * name corresponding to each prefix.
-     */
-    vec<ExtensionNameAndPrefix> extensionNameToPrefix;
-
-    /**
-     * A correspondence between an extension name and a prefix of operand and
-     * operation type values.
-     */
-    struct ExtensionNameAndPrefix {
-        /**
-         * The extension name.
-         *
-         * See {@link Extension::name} for the format specification.
-         */
-        string name;
-
-        /**
-         * The unique extension identifier within the model.
-         *
-         * See {@link Model::extensionNameToPrefix}.
-         */
-        uint16_t prefix;
-    };
-
-    /**
-     * Numeric values of extension operand and operation types have the
-     * following structure:
-     * - 16 high bits represent the "prefix", which corresponds uniquely to the
-     *   extension name.
-     * - 16 low bits represent the type ID within the extension.
-     */
-    enum ExtensionTypeEncoding : uint8_t {
-        HIGH_BITS_PREFIX = 16,
-        LOW_BITS_TYPE = 16,
-    };
-};
-
-/**
- * Describes the shape information of an output operand after execution.
- */
-struct OutputShape {
-    /**
-     * Dimensions of the operand.
-     */
-    vec<uint32_t> dimensions;
-
-    /**
-     * Whether the provided buffer size is sufficient for the output.
-     */
-    bool isSufficient;
-};
-
-/**
- * Specifies whether or not to measure timing information during execution.
- */
-enum MeasureTiming : int32_t {
-    NO  = 0,
-    YES = 1,
-};
-
-/**
-
- * Timing information measured during execution. Each time is a duration from
- * the beginning of some task to the end of that task, including time when that
- * task is not active (for example, preempted by some other task, or
- * waiting for some resource to become available).
- *
- * Times are measured in microseconds.
- * When a time is not available, it must be reported as UINT64_MAX.
- */
-struct Timing {
-    /** Execution time on device (not driver, which runs on host processor). */
-    uint64_t timeOnDevice;
-    /** Execution time in driver (including time on device). */
-    uint64_t timeInDriver;
-};
+%insert Timing
 
 /**
  * FmqRequestDatum is a single element of a serialized representation of an
@@ -680,46 +327,4 @@
     Timing executionTiming;
 };
 
-/**
- * Information about an extension.
- */
-struct Extension {
-    /**
-     * The extension name.
-     *
-     * The name must consist of lowercase latin letters, numbers, periods, and
-     * underscore signs. The name must contain at least one period.
-     *
-     * The name must start with the reverse domain name of the vendor.
-     *
-     * Example: com.google.test_extension
-     */
-    string name;
-
-    /**
-     * Information about an extension operand type.
-     */
-    struct OperandTypeInformation {
-        /**
-         * The extension operand type.
-         */
-        uint16_t type;
-
-        /**
-         * Indicates whether the extension operand type represents a tensor or
-         * a scalar.
-         */
-        bool isTensor;
-
-        /**
-         * The byte size of the operand (if scalar) or of a single element (if
-         * tensor).
-         */
-        uint32_t byteSize;
-    };
-
-    /**
-     * Information about operand types defined by the extension.
-     */
-    vec<OperandTypeInformation> operandTypes;
-};
+%insert Extension
diff --git a/neuralnetworks/1.2/utils/Android.bp b/neuralnetworks/1.2/utils/Android.bp
new file mode 100644
index 0000000..41281ee
--- /dev/null
+++ b/neuralnetworks/1.2/utils/Android.bp
@@ -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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_1_2",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal/1.2/"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "libfmq",
+    ],
+    export_static_lib_headers: [
+        "neuralnetworks_utils_hal_common",
+    ],
+    product_variables: {
+        debuggable: { // eng and userdebug builds
+            cflags: ["-DNN_DEBUGGABLE"],
+        },
+    },
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_1_2_test",
+    srcs: ["test/*.cpp"],
+    static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.2/utils/OWNERS b/neuralnetworks/1.2/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/1.2/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
new file mode 100644
index 0000000..ba3c1ba
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Callbacks.h
@@ -0,0 +1,89 @@
+/*
+ * 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_1_2_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+// 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::V1_2::utils {
+
+// Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
+// function returns with a non-null nn::SharedPreparedModel with a feature level of
+// nn::Version::ANDROID_Q. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with the output shapes and the timing information. On failure, this function returns with
+// the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes, const Timing& timing);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(V1_0::ErrorStatus status,
+                        const sp<V1_0::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+// A HIDL callback class to receive the results of IDevice::execute_1_2 asynchronously.
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(V1_0::ErrorStatus status) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes,
+                            const Timing& timing) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
new file mode 100644
index 0000000..272cee7
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Conversions.h
@@ -0,0 +1,109 @@
+/*
+ * 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_1_2_CONVERSIONS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_CONVERSIONS_H
+
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+namespace android::nn {
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType);
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_2::Capabilities::OperandPerformance& operandPerformance);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation);
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
+        const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand);
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model);
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape);
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming);
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing);
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension);
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation);
+GeneralResult<SharedHandle> unvalidatedConvert(const hardware::hidl_handle& handle);
+
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType);
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_2::Model& model);
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming);
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing);
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory);
+
+GeneralResult<std::vector<Extension>> convert(
+        const hardware::hidl_vec<hal::V1_2::Extension>& extensions);
+GeneralResult<std::vector<SharedHandle>> convert(
+        const hardware::hidl_vec<hardware::hidl_handle>& handles);
+GeneralResult<std::vector<OutputShape>> convert(
+        const hardware::hidl_vec<hal::V1_2::OutputShape>& outputShapes);
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
+        const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix);
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing);
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension);
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const nn::Extension::OperandTypeInformation& operandTypeInformation);
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle);
+
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Timing> convert(const nn::Timing& timing);
+
+nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions);
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes);
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request);
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status);
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+        const nn::ExecutionPreference& executionPreference);
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_CONVERSIONS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
new file mode 100644
index 0000000..b4bef5e
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Device.h
@@ -0,0 +1,112 @@
+/*
+ * 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_1_2_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_2::utils {
+
+// Retrieves the version string from the provided device object. On failure, this function returns
+// with the appropriate nn::GeneralError.
+nn::GeneralResult<std::string> getVersionStringFrom(V1_2::IDevice* device);
+
+// Retrieves the device type from the provided device object. On failure, this function returns with
+// the appropriate nn::GeneralError.
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(V1_2::IDevice* device);
+
+// Retrieves the extensions supported by the provided device object. On failure, this function
+// returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(V1_2::IDevice* device);
+
+// Retrieves the number of model cache files and data cache files needed by the provided device
+// object. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
+        V1_2::IDevice* device);
+
+// Class that adapts V1_2::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_2::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+           nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+           nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+           sp<V1_2::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::DeviceType kDeviceType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+    const sp<V1_2::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
new file mode 100644
index 0000000..9c66446
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_2::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+            hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              V1_0::Request request, hal::utils::RequestRelocation relocation,
+              V1_2::MeasureTiming measure);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const V1_0::Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const MeasureTiming kMeasure;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
new file mode 100644
index 0000000..dae1ff3
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstController.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2019 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_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <atomic>
+#include <chrono>
+#include <functional>
+#include <map>
+#include <memory>
+#include <mutex>
+#include <stack>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+/**
+ * The ExecutionBurstController class manages both the serialization and deserialization of data
+ * across FMQ, making it appear to the runtime as a regular synchronous inference. Additionally,
+ * this class manages the burst's memory cache.
+ */
+class ExecutionBurstController final
+    : public nn::IBurst,
+      public std::enable_shared_from_this<ExecutionBurstController> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using FallbackFunction = std::function<
+            nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>()>;
+
+    /**
+     * NN runtime memory cache.
+     *
+     * MemoryCache associates a Memory object with a slot number to be passed across FMQ. The
+     * ExecutionBurstServer can use this callback to retrieve a hidl_memory corresponding to the
+     * slot via HIDL.
+     *
+     * Whenever a hidl_memory object is copied, it will duplicate the underlying file descriptor.
+     * Because the NN runtime currently copies the hidl_memory on each execution, it is difficult to
+     * associate hidl_memory objects with previously cached hidl_memory objects. For this reason,
+     * callers of this class must pair each hidl_memory object with an associated key. For
+     * efficiency, if two hidl_memory objects represent the same underlying buffer, they must use
+     * the same key.
+     *
+     * This class is thread-safe.
+     */
+    class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+        struct PrivateConstructorTag {};
+
+      public:
+        using Task = std::function<void()>;
+        using Cleanup = base::ScopeGuard<Task>;
+        using SharedCleanup = std::shared_ptr<const Cleanup>;
+        using WeakCleanup = std::weak_ptr<const Cleanup>;
+
+        // Custom constructor to pre-allocate cache sizes.
+        MemoryCache();
+
+        /**
+         * Add a burst context to the MemoryCache object.
+         *
+         * If this method is called, it must be called before the MemoryCache::cacheMemory or
+         * MemoryCache::getMemory is used.
+         *
+         * @param burstContext Burst context to be added to the MemoryCache object.
+         */
+        void setBurstContext(sp<IBurstContext> burstContext);
+
+        /**
+         * Cache a memory object in the MemoryCache object.
+         *
+         * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+         * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+         *     "hold" object which preserves the cache as long as the hold object is alive.
+         */
+        std::pair<int32_t, SharedCleanup> cacheMemory(const nn::SharedMemory& memory);
+
+        /**
+         * Get the memory object corresponding to a slot identifier.
+         *
+         * @param slot Slot which identifies the memory object to retrieve.
+         * @return The memory object corresponding to slot, otherwise GeneralError.
+         */
+        nn::GeneralResult<nn::SharedMemory> getMemory(int32_t slot);
+
+      private:
+        void freeMemory(const nn::SharedMemory& memory);
+        int32_t allocateSlotLocked() REQUIRES(mMutex);
+
+        std::mutex mMutex;
+        std::condition_variable mCond;
+        sp<IBurstContext> mBurstContext GUARDED_BY(mMutex);
+        std::stack<int32_t, std::vector<int32_t>> mFreeSlots GUARDED_BY(mMutex);
+        std::map<nn::SharedMemory, int32_t> mMemoryIdToSlot GUARDED_BY(mMutex);
+        std::vector<nn::SharedMemory> mMemoryCache GUARDED_BY(mMutex);
+        std::vector<WeakCleanup> mCacheCleaner GUARDED_BY(mMutex);
+    };
+
+    /**
+     * HIDL Callback class to pass memory objects to the Burst server when given corresponding
+     * slots.
+     */
+    class ExecutionBurstCallback : public IBurstCallback {
+      public:
+        // Precondition: memoryCache must be non-null.
+        explicit ExecutionBurstCallback(const std::shared_ptr<MemoryCache>& memoryCache);
+
+        // See IBurstCallback::getMemories for information on this method.
+        Return<void> getMemories(const hidl_vec<int32_t>& slots, getMemories_cb cb) override;
+
+      private:
+        const std::weak_ptr<MemoryCache> kMemoryCache;
+    };
+
+    /**
+     * Creates a burst controller on a prepared model.
+     *
+     * @param preparedModel Model prepared for execution to execute on.
+     * @param pollingTimeWindow How much time (in microseconds) the ExecutionBurstController is
+     *     allowed to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+     *     latencies at the potential cost of more power usage.
+     * @return ExecutionBurstController Execution burst controller object.
+     */
+    static nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> create(
+            nn::SharedPreparedModel preparedModel, const sp<IPreparedModel>& hidlPreparedModel,
+            std::chrono::microseconds pollingTimeWindow);
+
+    ExecutionBurstController(PrivateConstructorTag tag, nn::SharedPreparedModel preparedModel,
+                             std::unique_ptr<RequestChannelSender> requestChannelSender,
+                             std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+                             sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+                             std::shared_ptr<MemoryCache> memoryCache,
+                             neuralnetworks::utils::DeathHandler deathHandler);
+
+    // See IBurst::cacheMemory for information on this method.
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+    // See IBurst::execute for information on this method.
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    // See IBurst::createReusableExecution for information on this method.
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    // If fallback is not nullptr, this method will invoke the fallback function to try another
+    // execution path if the packet could not be sent. Otherwise, failing to send the packet will
+    // result in an error.
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const std::vector<FmqRequestDatum>& requestPacket,
+            const hal::utils::RequestRelocation& relocation, FallbackFunction fallback) const;
+
+  private:
+    mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+    const nn::SharedPreparedModel kPreparedModel;
+    const std::unique_ptr<RequestChannelSender> mRequestChannelSender;
+    const std::unique_ptr<ResultChannelReceiver> mResultChannelReceiver;
+    const sp<ExecutionBurstCallback> mBurstCallback;
+    const sp<IBurstContext> mBurstContext;
+    const std::shared_ptr<MemoryCache> mMemoryCache;
+    // `kDeathHandler` must come after `mRequestChannelSender` and `mResultChannelReceiver` because
+    // it holds references to both objects.
+    const neuralnetworks::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_CONTROLLER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
new file mode 100644
index 0000000..f7926f5
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstServer.h
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2019 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_1_2_UTILS_EXECUTION_BURST_SERVER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/thread_annotations.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IBurstCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <optional>
+#include <thread>
+#include <tuple>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+/**
+ * The ExecutionBurstServer class is responsible for waiting for and deserializing a request object
+ * from a FMQ, performing the inference, and serializing the result back across another FMQ.
+ */
+class ExecutionBurstServer : public IBurstContext {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Class to cache the memory objects for a burst object.
+     *
+     * This class is thread-safe.
+     */
+    class MemoryCache {
+      public:
+        // Precondition: burstExecutor != nullptr
+        // Precondition: burstCallback != nullptr
+        MemoryCache(nn::SharedBurst burstExecutor, sp<IBurstCallback> burstCallback);
+
+        /**
+         * Get the cached memory objects corresponding to provided slot identifiers.
+         *
+         * If the slot entry is not present in the cache, this class will use IBurstCallback to
+         * retrieve those entries that are not present in the cache, then cache them.
+         *
+         * @param slots Identifiers of memory objects to be retrieved.
+         * @return A vector where each element is the memory object and a ref-counted cache "hold"
+         *     object to preserve the cache entry of the IBurst object as long as the "hold" object
+         *     is alive, otherwise GeneralError. Each element of the vector corresponds to the
+         *     element of slot.
+         */
+        nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
+        getCacheEntries(const std::vector<int32_t>& slots);
+
+        /**
+         * Remove an entry from the cache.
+         *
+         * @param slot Identifier of the memory object to be removed from the cache.
+         */
+        void removeCacheEntry(int32_t slot);
+
+      private:
+        nn::GeneralResult<void> ensureCacheEntriesArePresentLocked(
+                const std::vector<int32_t>& slots) REQUIRES(mMutex);
+        nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
+        getCacheEntryLocked(int32_t slot) REQUIRES(mMutex);
+        void addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) REQUIRES(mMutex);
+
+        std::mutex mMutex;
+        std::map<int32_t, std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> mCache
+                GUARDED_BY(mMutex);
+        nn::SharedBurst kBurstExecutor;
+        const sp<IBurstCallback> kBurstCallback;
+    };
+
+    /**
+     * Create automated context to manage FMQ-based executions.
+     *
+     * This function is intended to be used by a service to automatically:
+     * 1) Receive data from a provided FMQ
+     * 2) Execute a model with the given information
+     * 3) Send the result to the created FMQ
+     *
+     * @param callback Callback used to retrieve memories corresponding to unrecognized slots.
+     * @param requestChannel Input FMQ channel through which the client passes the request to the
+     *     service.
+     * @param resultChannel Output FMQ channel from which the client can retrieve the result of the
+     *     execution.
+     * @param burstExecutor Object which maintains a local cache of the memory pools and executes
+     *     using the cached memory pools.
+     * @param pollingTimeWindow How much time (in microseconds) the ExecutionBurstServer is allowed
+     *     to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+     *     latencies at the potential cost of more power usage.
+     * @return IBurstContext Handle to the burst context.
+     */
+    static nn::GeneralResult<sp<ExecutionBurstServer>> create(
+            const sp<IBurstCallback>& callback,
+            const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+            const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
+            std::chrono::microseconds pollingTimeWindow = std::chrono::microseconds{0});
+
+    ExecutionBurstServer(PrivateConstructorTag tag, const sp<IBurstCallback>& callback,
+                         std::unique_ptr<RequestChannelReceiver> requestChannel,
+                         std::unique_ptr<ResultChannelSender> resultChannel,
+                         nn::SharedBurst burstExecutor);
+    ~ExecutionBurstServer();
+
+    // Used by the NN runtime to preemptively remove any stored memory. See
+    // IBurstContext::freeMemory for more information.
+    Return<void> freeMemory(int32_t slot) override;
+
+  private:
+    // Work loop that will continue processing execution requests until the ExecutionBurstServer
+    // object is freed.
+    void task();
+
+    nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> execute(
+            const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
+            MeasureTiming measure);
+
+    std::thread mWorker;
+    std::atomic<bool> mTeardown{false};
+    const sp<IBurstCallback> mCallback;
+    const std::unique_ptr<RequestChannelReceiver> mRequestChannelReceiver;
+    const std::unique_ptr<ResultChannelSender> mResultChannelSender;
+    const nn::SharedBurst mBurstExecutor;
+    MemoryCache mMemoryCache;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_SERVER_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
new file mode 100644
index 0000000..c662bc3
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/ExecutionBurstUtils.h
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2019 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_1_2_UTILS_EXECUTION_BURST_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+/**
+ * Number of elements in the FMQ.
+ */
+constexpr const size_t kExecutionBurstChannelLength = 1024;
+
+/**
+ * Get how long the burst controller should poll while waiting for results to be returned.
+ *
+ * This time can be affected by the property "debug.nn.burst-controller-polling-window".
+ *
+ * @return Polling time in microseconds.
+ */
+std::chrono::microseconds getBurstControllerPollingTimeWindow();
+
+/**
+ * Get how long the burst server should poll while waiting for a request to be received.
+ *
+ * This time can be affected by the property "debug.nn.burst-server-polling-window".
+ *
+ * @return Polling time in microseconds.
+ */
+std::chrono::microseconds getBurstServerPollingTimeWindow();
+
+/**
+ * Function to serialize a request.
+ *
+ * @param request Request object without the pool information.
+ * @param measure Whether to collect timing information for the execution.
+ * @param memoryIds Slot identifiers corresponding to memory resources for the request.
+ * @return Serialized FMQ request data.
+ */
+std::vector<FmqRequestDatum> serialize(const V1_0::Request& request, MeasureTiming measure,
+                                       const std::vector<int32_t>& slots);
+
+/**
+ * Deserialize the FMQ request data.
+ *
+ * The three resulting fields are the Request object (where Request::pools is empty), slot
+ * identifiers (which are stand-ins for Request::pools), and whether timing information must be
+ * collected for the run.
+ *
+ * @param data Serialized FMQ request data.
+ * @return Request object if successfully deserialized, otherwise an error message.
+ */
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> deserialize(
+        const std::vector<FmqRequestDatum>& data);
+
+/**
+ * Function to serialize results.
+ *
+ * @param errorStatus Status of the execution.
+ * @param outputShapes Dynamic shapes of the output tensors.
+ * @param timing Timing information of the execution.
+ * @return Serialized FMQ result data.
+ */
+std::vector<FmqResultDatum> serialize(V1_0::ErrorStatus errorStatus,
+                                      const std::vector<OutputShape>& outputShapes, Timing timing);
+
+/**
+ * Deserialize the FMQ result data.
+ *
+ * The three resulting fields are the status of the execution, the dynamic shapes of the output
+ * tensors, and the timing information of the execution.
+ *
+ * @param data Serialized FMQ result data.
+ * @return Result object if successfully deserialized, otherwise an error message.
+ */
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<OutputShape>, Timing>> deserialize(
+        const std::vector<FmqResultDatum>& data);
+
+/**
+ * RequestChannelSender is responsible for serializing the result packet of information, sending it
+ * on the result channel, and signaling that the data is available.
+ */
+class RequestChannelSender final : public neuralnetworks::utils::IProtectedCallback {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Create the sending end of a request channel.
+     *
+     * @param channelLength Number of elements in the FMQ.
+     * @return A pair of ResultChannelReceiver and the FMQ descriptor on successful creation,
+     *     GeneralError otherwise.
+     */
+    static nn::GeneralResult<std::pair<std::unique_ptr<RequestChannelSender>,
+                                       const MQDescriptorSync<FmqRequestDatum>*>>
+    create(size_t channelLength);
+
+    /**
+     * Send the request to the channel.
+     *
+     * @param request Request object without the pool information.
+     * @param measure Whether to collect timing information for the execution.
+     * @param slots Slot identifiers corresponding to memory resources for the request.
+     * @return An empty `Result` on successful send, otherwise an error message.
+     */
+    nn::Result<void> send(const V1_0::Request& request, MeasureTiming measure,
+                          const std::vector<int32_t>& slots);
+
+    /**
+     * Method to mark the channel as invalid, causing all future calls to RequestChannelSender::send
+     * to immediately return false without attempting to send a message across the FMQ.
+     */
+    void notifyAsDeadObject() override;
+
+    // prefer calling RequestChannelSender::send
+    nn::Result<void> sendPacket(const std::vector<FmqRequestDatum>& packet);
+
+    RequestChannelSender(PrivateConstructorTag tag, size_t channelLength);
+
+  private:
+    MessageQueue<FmqRequestDatum, kSynchronizedReadWrite> mFmqRequestChannel;
+    std::atomic<bool> mValid{true};
+};
+
+/**
+ * RequestChannelReceiver is responsible for waiting on the channel until the packet is available,
+ * extracting the packet from the channel, and deserializing the packet.
+ *
+ * Because the receiver can wait on a packet that may never come (e.g., because the sending side of
+ * the packet has been closed), this object can be invalidated, unblocking the receiver.
+ */
+class RequestChannelReceiver final {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Create the receiving end of a request channel.
+     *
+     * @param requestChannel Descriptor for the request channel.
+     * @param pollingTimeWindow How much time (in microseconds) the RequestChannelReceiver is
+     *     allowed to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+     *     latencies at the potential cost of more power usage.
+     * @return RequestChannelReceiver on successful creation, nullptr otherwise.
+     */
+    static nn::GeneralResult<std::unique_ptr<RequestChannelReceiver>> create(
+            const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+            std::chrono::microseconds pollingTimeWindow);
+
+    /**
+     * Get the request from the channel.
+     *
+     * This method will block until either:
+     * 1) The packet has been retrieved, or
+     * 2) The receiver has been invalidated
+     *
+     * @return Request object if successfully received, an appropriate message if error or if the
+     *     receiver object was invalidated.
+     */
+    nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, MeasureTiming>> getBlocking();
+
+    /**
+     * Method to mark the channel as invalid, unblocking any current or future calls to
+     * RequestChannelReceiver::getBlocking.
+     */
+    void invalidate();
+
+    RequestChannelReceiver(PrivateConstructorTag tag,
+                           const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+                           std::chrono::microseconds pollingTimeWindow);
+
+  private:
+    nn::Result<std::vector<FmqRequestDatum>> getPacketBlocking();
+
+    MessageQueue<FmqRequestDatum, kSynchronizedReadWrite> mFmqRequestChannel;
+    std::atomic<bool> mTeardown{false};
+    const std::chrono::microseconds kPollingTimeWindow;
+};
+
+/**
+ * ResultChannelSender is responsible for serializing the result packet of information, sending it
+ * on the result channel, and signaling that the data is available.
+ */
+class ResultChannelSender final {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Create the sending end of a result channel.
+     *
+     * @param resultChannel Descriptor for the result channel.
+     * @return ResultChannelSender on successful creation, nullptr otherwise.
+     */
+    static nn::GeneralResult<std::unique_ptr<ResultChannelSender>> create(
+            const MQDescriptorSync<FmqResultDatum>& resultChannel);
+
+    /**
+     * Send the result to the channel.
+     *
+     * @param errorStatus Status of the execution.
+     * @param outputShapes Dynamic shapes of the output tensors.
+     * @param timing Timing information of the execution.
+     */
+    void send(V1_0::ErrorStatus errorStatus, const std::vector<OutputShape>& outputShapes,
+              Timing timing);
+
+    // prefer calling ResultChannelSender::send
+    void sendPacket(const std::vector<FmqResultDatum>& packet);
+
+    ResultChannelSender(PrivateConstructorTag tag,
+                        const MQDescriptorSync<FmqResultDatum>& resultChannel);
+
+  private:
+    MessageQueue<FmqResultDatum, kSynchronizedReadWrite> mFmqResultChannel;
+};
+
+/**
+ * ResultChannelReceiver is responsible for waiting on the channel until the packet is available,
+ * extracting the packet from the channel, and deserializing the packet.
+ *
+ * Because the receiver can wait on a packet that may never come (e.g., because the sending side of
+ * the packet has been closed), this object can be invalidated, unblocking the receiver.
+ */
+class ResultChannelReceiver final : public neuralnetworks::utils::IProtectedCallback {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Create the receiving end of a result channel.
+     *
+     * @param channelLength Number of elements in the FMQ.
+     * @param pollingTimeWindow How much time (in microseconds) the ResultChannelReceiver is allowed
+     *     to poll the FMQ before waiting on the blocking futex. Polling may result in lower
+     *     latencies at the potential cost of more power usage.
+     * @return A pair of ResultChannelReceiver and the FMQ descriptor on successful creation, or
+     *     GeneralError otherwise.
+     */
+    static nn::GeneralResult<std::pair<std::unique_ptr<ResultChannelReceiver>,
+                                       const MQDescriptorSync<FmqResultDatum>*>>
+    create(size_t channelLength, std::chrono::microseconds pollingTimeWindow);
+
+    /**
+     * Get the result from the channel.
+     *
+     * This method will block until either:
+     * 1) The packet has been retrieved, or
+     * 2) The receiver has been invalidated
+     *
+     * @return Result object if successfully received, otherwise an appropriate message if error or
+     *     if the receiver object was invalidated.
+     */
+    nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<OutputShape>, Timing>> getBlocking();
+
+    /**
+     * Method to mark the channel as invalid, unblocking any current or future calls to
+     * ResultChannelReceiver::getBlocking.
+     */
+    void notifyAsDeadObject() override;
+
+    // prefer calling ResultChannelReceiver::getBlocking
+    nn::Result<std::vector<FmqResultDatum>> getPacketBlocking();
+
+    ResultChannelReceiver(PrivateConstructorTag tag, size_t channelLength,
+                          std::chrono::microseconds pollingTimeWindow);
+
+  private:
+    MessageQueue<FmqResultDatum, kSynchronizedReadWrite> mFmqResultChannel;
+    std::atomic<bool> mValid{true};
+    const std::chrono::microseconds kPollingTimeWindow;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_EXECUTION_BURST_UTILS_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
new file mode 100644
index 0000000..35abd79
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/PreparedModel.h
@@ -0,0 +1,86 @@
+/*
+ * 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_1_2_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_2::utils {
+
+// Class that adapts V1_2::IPreparedModel to nn::IPreparedModel.
+class PreparedModel final : public nn::IPreparedModel,
+                            public std::enable_shared_from_this<PreparedModel> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_2::IPreparedModel> preparedModel, bool executeSynchronously);
+
+    PreparedModel(PrivateConstructorTag tag, bool executeSynchronously,
+                  sp<V1_2::IPreparedModel> preparedModel, hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const V1_0::Request& request, MeasureTiming measure,
+            const hal::utils::RequestRelocation& relocation) const;
+
+  private:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
+            const V1_0::Request& request, MeasureTiming measure) const;
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeAsynchronously(
+            const V1_0::Request& request, MeasureTiming measure) const;
+
+    const bool kExecuteSynchronously;
+    const sp<V1_2::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h
new file mode 100644
index 0000000..44f004f
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Service.h
@@ -0,0 +1,31 @@
+/*
+ * 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_1_2_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
new file mode 100644
index 0000000..09691b6
--- /dev/null
+++ b/neuralnetworks/1.2/utils/include/nnapi/hal/1.2/Utils.h
@@ -0,0 +1,80 @@
+/*
+ * 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_1_2_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_H
+
+#include "nnapi/hal/1.2/Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.1/Utils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <limits>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+using CacheToken = hidl_array<uint8_t, static_cast<size_t>(Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+using V1_1::utils::kDefaultExecutionPreference;
+
+constexpr auto kDefaultMesaureTiming = MeasureTiming::NO;
+constexpr auto kNoTiming = Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+                                  .timeInDriver = std::numeric_limits<uint64_t>::max()};
+constexpr auto kVersion = nn::Version::ANDROID_Q;
+
+template <typename Type>
+nn::Result<void> validate(const Type& halObject) {
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
+    }
+    return {};
+}
+
+template <typename Type>
+bool valid(const Type& halObject) {
+    const auto result = utils::validate(halObject);
+    if (!result.has_value()) {
+        LOG(ERROR) << result.error();
+    }
+    return result.has_value();
+}
+
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+        -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+    return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_H
diff --git a/neuralnetworks/1.2/utils/src/Assertions.cpp b/neuralnetworks/1.2/utils/src/Assertions.cpp
new file mode 100644
index 0000000..9d9716a
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Assertions.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Types.h>
+#include <type_traits>
+
+namespace {
+
+#define COMPARE_ENUMS_TYPES(type)                                                                  \
+    static_assert(std::is_same_v<                                                                  \
+                          std::underlying_type_t<::android::hardware::neuralnetworks::V1_2::type>, \
+                          std::underlying_type_t<::android::nn::type>>,                            \
+                  "::android::hardware::neuralnetworks::V1_2::" #type                              \
+                  " does not have the same underlying type as ::android::nn::" #type)
+
+COMPARE_ENUMS_TYPES(OperandType);
+COMPARE_ENUMS_TYPES(OperationType);
+COMPARE_ENUMS_TYPES(DeviceType);
+COMPARE_ENUMS_TYPES(MeasureTiming);
+
+#undef COMPARE_ENUMS_TYPES
+
+#define COMPARE_ENUMS_FULL(symbol, type)                                                          \
+    static_assert(                                                                                \
+            static_cast<std::underlying_type_t<::android::hardware::neuralnetworks::V1_2::type>>( \
+                    ::android::hardware::neuralnetworks::V1_2::type::symbol) ==                   \
+                    static_cast<std::underlying_type_t<::android::nn::type>>(                     \
+                            ::android::nn::type::symbol),                                         \
+            "::android::hardware::neuralnetworks::V1_2::" #type "::" #symbol                      \
+            " does not match ::android::nn::" #type "::" #symbol)
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperandType)
+
+COMPARE_ENUMS(FLOAT32);
+COMPARE_ENUMS(INT32);
+COMPARE_ENUMS(UINT32);
+COMPARE_ENUMS(TENSOR_FLOAT32);
+COMPARE_ENUMS(TENSOR_INT32);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM);
+COMPARE_ENUMS(BOOL);
+COMPARE_ENUMS(TENSOR_QUANT16_SYMM);
+COMPARE_ENUMS(TENSOR_FLOAT16);
+COMPARE_ENUMS(TENSOR_BOOL8);
+COMPARE_ENUMS(FLOAT16);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM_PER_CHANNEL);
+COMPARE_ENUMS(TENSOR_QUANT16_ASYMM);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM);
+COMPARE_ENUMS(OEM);
+COMPARE_ENUMS(TENSOR_OEM_BYTE);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperationType)
+
+COMPARE_ENUMS(ADD);
+COMPARE_ENUMS(AVERAGE_POOL_2D);
+COMPARE_ENUMS(CONCATENATION);
+COMPARE_ENUMS(CONV_2D);
+COMPARE_ENUMS(DEPTHWISE_CONV_2D);
+COMPARE_ENUMS(DEPTH_TO_SPACE);
+COMPARE_ENUMS(DEQUANTIZE);
+COMPARE_ENUMS(EMBEDDING_LOOKUP);
+COMPARE_ENUMS(FLOOR);
+COMPARE_ENUMS(FULLY_CONNECTED);
+COMPARE_ENUMS(HASHTABLE_LOOKUP);
+COMPARE_ENUMS(L2_NORMALIZATION);
+COMPARE_ENUMS(L2_POOL_2D);
+COMPARE_ENUMS(LOCAL_RESPONSE_NORMALIZATION);
+COMPARE_ENUMS(LOGISTIC);
+COMPARE_ENUMS(LSH_PROJECTION);
+COMPARE_ENUMS(LSTM);
+COMPARE_ENUMS(MAX_POOL_2D);
+COMPARE_ENUMS(MUL);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+COMPARE_ENUMS(RESHAPE);
+COMPARE_ENUMS(RESIZE_BILINEAR);
+COMPARE_ENUMS(RNN);
+COMPARE_ENUMS(SOFTMAX);
+COMPARE_ENUMS(SPACE_TO_DEPTH);
+COMPARE_ENUMS(SVDF);
+COMPARE_ENUMS(TANH);
+COMPARE_ENUMS(BATCH_TO_SPACE_ND);
+COMPARE_ENUMS(DIV);
+COMPARE_ENUMS(MEAN);
+COMPARE_ENUMS(PAD);
+COMPARE_ENUMS(SPACE_TO_BATCH_ND);
+COMPARE_ENUMS(SQUEEZE);
+COMPARE_ENUMS(STRIDED_SLICE);
+COMPARE_ENUMS(SUB);
+COMPARE_ENUMS(TRANSPOSE);
+COMPARE_ENUMS(ABS);
+COMPARE_ENUMS(ARGMAX);
+COMPARE_ENUMS(ARGMIN);
+COMPARE_ENUMS(AXIS_ALIGNED_BBOX_TRANSFORM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(BOX_WITH_NMS_LIMIT);
+COMPARE_ENUMS(CAST);
+COMPARE_ENUMS(CHANNEL_SHUFFLE);
+COMPARE_ENUMS(DETECTION_POSTPROCESSING);
+COMPARE_ENUMS(EQUAL);
+COMPARE_ENUMS(EXP);
+COMPARE_ENUMS(EXPAND_DIMS);
+COMPARE_ENUMS(GATHER);
+COMPARE_ENUMS(GENERATE_PROPOSALS);
+COMPARE_ENUMS(GREATER);
+COMPARE_ENUMS(GREATER_EQUAL);
+COMPARE_ENUMS(GROUPED_CONV_2D);
+COMPARE_ENUMS(HEATMAP_MAX_KEYPOINT);
+COMPARE_ENUMS(INSTANCE_NORMALIZATION);
+COMPARE_ENUMS(LESS);
+COMPARE_ENUMS(LESS_EQUAL);
+COMPARE_ENUMS(LOG);
+COMPARE_ENUMS(LOGICAL_AND);
+COMPARE_ENUMS(LOGICAL_NOT);
+COMPARE_ENUMS(LOGICAL_OR);
+COMPARE_ENUMS(LOG_SOFTMAX);
+COMPARE_ENUMS(MAXIMUM);
+COMPARE_ENUMS(MINIMUM);
+COMPARE_ENUMS(NEG);
+COMPARE_ENUMS(NOT_EQUAL);
+COMPARE_ENUMS(PAD_V2);
+COMPARE_ENUMS(POW);
+COMPARE_ENUMS(PRELU);
+COMPARE_ENUMS(QUANTIZE);
+COMPARE_ENUMS(QUANTIZED_16BIT_LSTM);
+COMPARE_ENUMS(RANDOM_MULTINOMIAL);
+COMPARE_ENUMS(REDUCE_ALL);
+COMPARE_ENUMS(REDUCE_ANY);
+COMPARE_ENUMS(REDUCE_MAX);
+COMPARE_ENUMS(REDUCE_MIN);
+COMPARE_ENUMS(REDUCE_PROD);
+COMPARE_ENUMS(REDUCE_SUM);
+COMPARE_ENUMS(ROI_ALIGN);
+COMPARE_ENUMS(ROI_POOLING);
+COMPARE_ENUMS(RSQRT);
+COMPARE_ENUMS(SELECT);
+COMPARE_ENUMS(SIN);
+COMPARE_ENUMS(SLICE);
+COMPARE_ENUMS(SPLIT);
+COMPARE_ENUMS(SQRT);
+COMPARE_ENUMS(TILE);
+COMPARE_ENUMS(TOPK_V2);
+COMPARE_ENUMS(TRANSPOSE_CONV_2D);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(RESIZE_NEAREST_NEIGHBOR);
+COMPARE_ENUMS(OEM_OPERATION);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, DeviceType)
+
+COMPARE_ENUMS(OTHER);
+COMPARE_ENUMS(CPU);
+COMPARE_ENUMS(GPU);
+COMPARE_ENUMS(ACCELERATOR);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, MeasureTiming)
+
+COMPARE_ENUMS(NO);
+COMPARE_ENUMS(YES);
+
+#undef COMPARE_ENUMS
+
+#undef COMPARE_ENUMS_FULL
+
+}  // anonymous namespace
diff --git a/neuralnetworks/1.2/utils/src/Callbacks.cpp b/neuralnetworks/1.2/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..9f54bb1
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Callbacks.cpp
@@ -0,0 +1,124 @@
+/*
+ * 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 "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+// 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::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        V1_0::ErrorStatus status, const sp<V1_0::IPreparedModel>& preparedModel) {
+    if (const auto dynamicPreparedModel =
+                V1_2::IPreparedModel::castFrom(preparedModel).withDefault(nullptr)) {
+        return V1_2::utils::prepareModelCallback(status, dynamicPreparedModel);
+    }
+    return V1_0::utils::prepareModelCallback(status, preparedModel);
+}
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResultsHelper(const hidl_vec<OutputShape>& outputShapes,
+                                     const Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+}  // namespace
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        V1_0::ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        V1_0::ErrorStatus status, const hidl_vec<OutputShape>& outputShapes, const Timing& timing) {
+    if (status == V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+        auto canonicalOutputShapes =
+                nn::convert(outputShapes).value_or(std::vector<nn::OutputShape>{});
+        return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+               << "execution failed with " << toString(status);
+    }
+    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    return hal::utils::makeExecutionFailure(
+            convertExecutionGeneralResultsHelper(outputShapes, timing));
+}
+
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
+                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
+                                               const sp<IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
+    mData.put(V1_0::utils::executionCallback(status));
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
+                                           const hidl_vec<OutputShape>& outputShapes,
+                                           const Timing& timing) {
+    mData.put(executionCallback(status, outputShapes, timing));
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Conversions.cpp b/neuralnetworks/1.2/utils/src/Conversions.cpp
new file mode 100644
index 0000000..29945b7
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Conversions.cpp
@@ -0,0 +1,604 @@
+/*
+ * 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 "Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <algorithm>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <type_traits>
+#include <utility>
+
+#include "Utils.h"
+
+namespace {
+
+template <typename Type>
+constexpr std::underlying_type_t<Type> underlyingType(Type value) {
+    return static_cast<std::underlying_type_t<Type>>(value);
+}
+
+using HalDuration = std::chrono::duration<uint64_t, std::micro>;
+
+}  // namespace
+
+namespace android::nn {
+namespace {
+
+using hardware::hidl_handle;
+using hardware::hidl_vec;
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    NN_TRY(hal::V1_2::utils::compliantVersion(canonical));
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
+}
+
+}  // anonymous namespace
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_2::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_2::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+GeneralResult<DeviceType> unvalidatedConvert(const hal::V1_2::DeviceType& deviceType) {
+    return static_cast<DeviceType>(deviceType);
+}
+
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_2::Capabilities& capabilities) {
+    const bool validOperandTypes = std::all_of(
+            capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
+            [](const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
+                return validatedConvert(operandPerformance.type).has_value();
+            });
+    if (!validOperandTypes) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Invalid OperandType when converting OperandPerformance in Capabilities";
+    }
+
+    const auto relaxedFloat32toFloat16PerformanceScalar =
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar));
+    const auto relaxedFloat32toFloat16PerformanceTensor =
+            NN_TRY(unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor));
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
+
+    auto table = NN_TRY(hal::utils::makeGeneralFailure(
+            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+            nn::ErrorStatus::GENERAL_FAILURE));
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = relaxedFloat32toFloat16PerformanceScalar,
+            .relaxedFloat32toFloat16PerformanceTensor = relaxedFloat32toFloat16PerformanceTensor,
+            .operandPerformance = std::move(table),
+    };
+}
+
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_2::Capabilities::OperandPerformance& operandPerformance) {
+    return Capabilities::OperandPerformance{
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
+    };
+}
+
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_2::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
+        const hal::V1_2::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+    return Operand::SymmPerChannelQuantParams{
+            .scales = symmPerChannelQuantParams.scales,
+            .channelDim = symmPerChannelQuantParams.channelDim,
+    };
+}
+
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_2::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const hal::V1_2::Operand::ExtraParams& extraParams) {
+    using Discriminator = hal::V1_2::Operand::ExtraParams::hidl_discriminator;
+    switch (extraParams.getDiscriminator()) {
+        case Discriminator::none:
+            return Operand::NoParams{};
+        case Discriminator::channelQuant:
+            return unvalidatedConvert(extraParams.channelQuant());
+        case Discriminator::extension:
+            return extraParams.extension();
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Unrecognized Operand::ExtraParams discriminator: "
+           << underlyingType(extraParams.getDiscriminator());
+}
+
+GeneralResult<Model> unvalidatedConvert(const hal::V1_2::Model& model) {
+    auto operations = NN_TRY(unvalidatedConvert(model.operations));
+
+    // Verify number of consumers.
+    const auto numberOfConsumers =
+            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]) {
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found " << model.operands[i].numberOfConsumers;
+        }
+    }
+
+    auto main = Model::Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(model.operands)),
+            .operations = std::move(operations),
+            .inputIndexes = model.inputIndexes,
+            .outputIndexes = model.outputIndexes,
+    };
+
+    return Model{
+            .main = std::move(main),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const hal::V1_2::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
+    return Model::ExtensionNameAndPrefix{
+            .name = extensionNameAndPrefix.name,
+            .prefix = extensionNameAndPrefix.prefix,
+    };
+}
+
+GeneralResult<OutputShape> unvalidatedConvert(const hal::V1_2::OutputShape& outputShape) {
+    return OutputShape{
+            .dimensions = outputShape.dimensions,
+            .isSufficient = outputShape.isSufficient,
+    };
+}
+
+GeneralResult<MeasureTiming> unvalidatedConvert(const hal::V1_2::MeasureTiming& measureTiming) {
+    return static_cast<MeasureTiming>(measureTiming);
+}
+
+GeneralResult<Timing> unvalidatedConvert(const hal::V1_2::Timing& timing) {
+    constexpr uint64_t kMaxTiming = std::chrono::floor<HalDuration>(Duration::max()).count();
+    constexpr auto convertTiming = [](uint64_t halTiming) -> OptionalDuration {
+        constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
+        if (halTiming == kNoTiming) {
+            return {};
+        }
+        if (halTiming > kMaxTiming) {
+            return Duration::max();
+        }
+        return HalDuration{halTiming};
+    };
+    return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
+                  .timeInDriver = convertTiming(timing.timeInDriver)};
+}
+
+GeneralResult<Extension> unvalidatedConvert(const hal::V1_2::Extension& extension) {
+    return Extension{
+            .name = extension.name,
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
+    };
+}
+
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const hal::V1_2::Extension::OperandTypeInformation& operandTypeInformation) {
+    return Extension::OperandTypeInformation{
+            .type = operandTypeInformation.type,
+            .isTensor = operandTypeInformation.isTensor,
+            .byteSize = operandTypeInformation.byteSize,
+    };
+}
+
+GeneralResult<SharedHandle> unvalidatedConvert(const hidl_handle& hidlHandle) {
+    if (hidlHandle.getNativeHandle() == nullptr) {
+        return nullptr;
+    }
+    auto handle = NN_TRY(hal::utils::sharedHandleFromNativeHandle(hidlHandle.getNativeHandle()));
+    return std::make_shared<const Handle>(std::move(handle));
+}
+
+GeneralResult<DeviceType> convert(const hal::V1_2::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_2::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_2::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<MeasureTiming> convert(const hal::V1_2::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+GeneralResult<Timing> convert(const hal::V1_2::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+GeneralResult<SharedMemory> convert(const hardware::hidl_memory& memory) {
+    return validatedConvert(memory);
+}
+
+GeneralResult<std::vector<Extension>> convert(const hidl_vec<hal::V1_2::Extension>& extensions) {
+    return validatedConvert(extensions);
+}
+
+GeneralResult<std::vector<SharedHandle>> convert(const hidl_vec<hidl_handle>& handles) {
+    return validatedConvert(handles);
+}
+
+GeneralResult<std::vector<OutputShape>> convert(
+        const hidl_vec<hal::V1_2::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
+}
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using utils::unvalidatedConvert;
+
+nn::GeneralResult<V1_0::OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& lifetime) {
+    return V1_0::utils::unvalidatedConvert(lifetime);
+}
+
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo) {
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
+}
+
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+    return V1_0::utils::unvalidatedConvert(location);
+}
+
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
+}
+
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
+}
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(nn::Operand::NoParams /*noParams*/) {
+    return Operand::ExtraParams{};
+}
+
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
+        const nn::Operand::SymmPerChannelQuantParams& channelQuant) {
+    Operand::ExtraParams ret;
+    ret.channelQuant(NN_TRY(unvalidatedConvert(channelQuant)));
+    return ret;
+}
+
+nn::GeneralResult<Operand::ExtraParams> makeExtraParams(
+        const nn::Operand::ExtensionParams& extension) {
+    Operand::ExtraParams ret;
+    ret.extension(extension);
+    return ret;
+}
+
+template <typename Type>
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+}  // anonymous namespace
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<DeviceType> unvalidatedConvert(const nn::DeviceType& deviceType) {
+    switch (deviceType) {
+        case nn::DeviceType::UNKNOWN:
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Invalid DeviceType UNKNOWN";
+        case nn::DeviceType::OTHER:
+        case nn::DeviceType::CPU:
+        case nn::DeviceType::GPU:
+        case nn::DeviceType::ACCELERATOR:
+            return static_cast<DeviceType>(deviceType);
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid DeviceType " << underlyingType(deviceType);
+}
+
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
+    std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
+    operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
+    std::copy_if(capabilities.operandPerformance.asVector().begin(),
+                 capabilities.operandPerformance.asVector().end(),
+                 std::back_inserter(operandPerformance),
+                 [](const nn::Capabilities::OperandPerformance& operandPerformance) {
+                     return compliantVersion(operandPerformance.type).has_value();
+                 });
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
+    };
+}
+
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance) {
+    return Capabilities::OperandPerformance{
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
+    };
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+nn::GeneralResult<SymmPerChannelQuantParams> unvalidatedConvert(
+        const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+    return SymmPerChannelQuantParams{
+            .scales = symmPerChannelQuantParams.scales,
+            .channelDim = symmPerChannelQuantParams.channelDim,
+    };
+}
+
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .numberOfConsumers = 0,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+nn::GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
+    return std::visit([](const auto& x) { return makeExtraParams(x); }, extraParams);
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+    if (!hal::utils::hasNoPointerData(model)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    auto operands = NN_TRY(unvalidatedConvert(model.main.operands));
+
+    // Update number of consumers.
+    const auto numberOfConsumers =
+            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];
+    }
+
+    return Model{
+            .operands = std::move(operands),
+            .operations = NN_TRY(unvalidatedConvert(model.main.operations)),
+            .inputIndexes = model.main.inputIndexes,
+            .outputIndexes = model.main.outputIndexes,
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+nn::GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
+    return Model::ExtensionNameAndPrefix{
+            .name = extensionNameAndPrefix.name,
+            .prefix = extensionNameAndPrefix.prefix,
+    };
+}
+
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape) {
+    return OutputShape{.dimensions = outputShape.dimensions,
+                       .isSufficient = outputShape.isSufficient};
+}
+
+nn::GeneralResult<MeasureTiming> unvalidatedConvert(const nn::MeasureTiming& measureTiming) {
+    return static_cast<MeasureTiming>(measureTiming);
+}
+
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
+    constexpr auto convertTiming = [](nn::OptionalDuration canonicalTiming) -> uint64_t {
+        constexpr uint64_t kNoTiming = std::numeric_limits<uint64_t>::max();
+        if (!canonicalTiming.has_value()) {
+            return kNoTiming;
+        }
+        return std::chrono::ceil<HalDuration>(*canonicalTiming).count();
+    };
+    return Timing{.timeOnDevice = convertTiming(timing.timeOnDevice),
+                  .timeInDriver = convertTiming(timing.timeInDriver)};
+}
+
+nn::GeneralResult<Extension> unvalidatedConvert(const nn::Extension& extension) {
+    return Extension{
+            .name = extension.name,
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
+    };
+}
+
+nn::GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const nn::Extension::OperandTypeInformation& operandTypeInformation) {
+    return Extension::OperandTypeInformation{
+            .type = operandTypeInformation.type,
+            .isTensor = operandTypeInformation.isTensor,
+            .byteSize = operandTypeInformation.byteSize,
+    };
+}
+
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    if (handle == nullptr) {
+        return {};
+    }
+    return hal::utils::hidlHandleFromSharedHandle(*handle);
+}
+
+nn::GeneralResult<DeviceType> convert(const nn::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+nn::GeneralResult<hidl_vec<Extension>> convert(const std::vector<nn::Extension>& extensions) {
+    return validatedConvert(extensions);
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles) {
+    return validatedConvert(handles);
+}
+
+nn::GeneralResult<hidl_vec<OutputShape>> convert(const std::vector<nn::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
+}
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return V1_1::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_0::Request> convert(const nn::Request& request) {
+    return V1_1::utils::convert(request);
+}
+
+nn::GeneralResult<V1_0::ErrorStatus> convert(const nn::ErrorStatus& status) {
+    return V1_1::utils::convert(status);
+}
+
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+        const nn::ExecutionPreference& executionPreference) {
+    return V1_1::utils::convert(executionPreference);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Device.cpp b/neuralnetworks/1.2/utils/src/Device.cpp
new file mode 100644
index 0000000..9fe0de2
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Device.cpp
@@ -0,0 +1,288 @@
+/*
+ * 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 "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_2::utils {
+namespace {
+
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(V1_0::ErrorStatus status,
+                                                         const Capabilities& capabilities) {
+    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    return nn::convert(capabilities);
+}
+
+nn::GeneralResult<std::string> versionStringCallback(V1_0::ErrorStatus status,
+                                                     const hidl_string& versionString) {
+    HANDLE_HAL_STATUS(status) << "getVersionString failed with " << toString(status);
+    return versionString;
+}
+
+nn::GeneralResult<nn::DeviceType> deviceTypeCallback(V1_0::ErrorStatus status,
+                                                     DeviceType deviceType) {
+    HANDLE_HAL_STATUS(status) << "getDeviceType failed with " << toString(status);
+    return nn::convert(deviceType);
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> supportedExtensionsCallback(
+        V1_0::ErrorStatus status, const hidl_vec<Extension>& extensions) {
+    HANDLE_HAL_STATUS(status) << "getExtensions failed with " << toString(status);
+    return nn::convert(extensions);
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> numberOfCacheFilesNeededCallback(
+        V1_0::ErrorStatus status, uint32_t numModelCache, uint32_t numDataCache) {
+    HANDLE_HAL_STATUS(status) << "getNumberOfCacheFilesNeeded failed with " << toString(status);
+    if (numModelCache > nn::kMaxNumberOfCacheFiles) {
+        return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
+                             "than allowed max ("
+                          << numModelCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
+    }
+    if (numDataCache > nn::kMaxNumberOfCacheFiles) {
+        return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numDataCache files greater "
+                             "than allowed max ("
+                          << numDataCache << " vs " << nn::kMaxNumberOfCacheFiles << ")";
+    }
+    return std::make_pair(numModelCache, numDataCache);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(capabilitiesCallback);
+
+    const auto ret = device->getCapabilities_1_2(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+}  // namespace
+
+nn::GeneralResult<std::string> getVersionStringFrom(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(versionStringCallback);
+
+    const auto ret = device->getVersionString(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(deviceTypeCallback);
+
+    const auto ret = device->getType(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(supportedExtensionsCallback);
+
+    const auto ret = device->getSupportedExtensions(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
+        V1_2::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(numberOfCacheFilesNeededCallback);
+
+    const auto ret = device->getNumberOfCacheFilesNeeded(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_2::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_2::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_2::utils::Device::create must have non-null device";
+    }
+
+    auto versionString = NN_TRY(getVersionStringFrom(device.get()));
+    const auto deviceType = NN_TRY(getDeviceTypeFrom(device.get()));
+    auto extensions = NN_TRY(getSupportedExtensionsFrom(device.get()));
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+    const auto numberOfCacheFilesNeeded = NN_TRY(getNumberOfCacheFilesNeededFrom(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(
+            PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+            std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+            std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+               nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+               nn::Capabilities capabilities,
+               std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_2::IDevice> device,
+               hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kDeviceType(deviceType),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_Q;
+}
+
+nn::DeviceType Device::getType() const {
+    return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    auto cb = hal::utils::CallbackValue(V1_0::utils::supportedOperationsCallback);
+
+    const auto ret = kDevice->getSupportedOperations_1_2(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority /*priority*/,
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(convert(preference));
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = CacheToken{token};
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel_1_2(hidlModel, hidlPreference, hidlModelCache,
+                                               hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = CacheToken{token};
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModelFromCache(hidlModelCache, hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IDevice::allocate not supported on 1.2 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Execution.cpp b/neuralnetworks/1.2/utils/src/Execution.cpp
new file mode 100644
index 0000000..18d1c90
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Execution.cpp
@@ -0,0 +1,74 @@
+/*
+ * 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.
+ */
+
+#include "Execution.h"
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_2::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+        hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_2::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, V1_0::Request request,
+                     hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kPreparedModel->executeInternal(kRequest, kMeasure, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& /*waitFor*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on 1.2 HAL service";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
new file mode 100644
index 0000000..b4b6f68
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstController.cpp
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "ExecutionBurstController"
+
+#include "ExecutionBurstController.h"
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+#include <memory>
+#include <string>
+#include <thread>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Tracing.h"
+#include "Utils.h"
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+class BurstExecution final : public nn::IExecution,
+                             public std::enable_shared_from_this<BurstExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+            std::shared_ptr<const ExecutionBurstController> controller,
+            std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+            std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+    BurstExecution(PrivateConstructorTag tag,
+                   std::shared_ptr<const ExecutionBurstController> controller,
+                   std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+                   std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const ExecutionBurstController> kController;
+    const std::vector<FmqRequestDatum> kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const std::vector<ExecutionBurstController::OptionalCacheHold> kCacheHolds;
+};
+
+nn::GeneralResult<sp<IBurstContext>> executionBurstResultCallback(
+        V1_0::ErrorStatus status, const sp<IBurstContext>& burstContext) {
+    HANDLE_HAL_STATUS(status) << "IPreparedModel::configureExecutionBurst failed with status "
+                              << toString(status);
+    if (burstContext == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "IPreparedModel::configureExecutionBurst returned nullptr for burst";
+    }
+    return burstContext;
+}
+
+nn::GeneralResult<hidl_vec<hidl_memory>> getMemoriesHelper(
+        const hidl_vec<int32_t>& slots,
+        const std::shared_ptr<ExecutionBurstController::MemoryCache>& memoryCache) {
+    hidl_vec<hidl_memory> memories(slots.size());
+    for (size_t i = 0; i < slots.size(); ++i) {
+        const int32_t slot = slots[i];
+        const auto memory = NN_TRY(memoryCache->getMemory(slot));
+        memories[i] = NN_TRY(V1_0::utils::unvalidatedConvert(memory));
+        if (!memories[i].valid()) {
+            return NN_ERROR() << "memory at slot " << slot << " is invalid";
+        }
+    }
+    return memories;
+}
+
+}  // namespace
+
+// MemoryCache methods
+
+ExecutionBurstController::MemoryCache::MemoryCache() {
+    constexpr size_t kPreallocatedCount = 1024;
+    std::vector<int32_t> freeSlotsSpace;
+    freeSlotsSpace.reserve(kPreallocatedCount);
+    mFreeSlots = std::stack<int32_t, std::vector<int32_t>>(std::move(freeSlotsSpace));
+    mMemoryCache.reserve(kPreallocatedCount);
+    mCacheCleaner.reserve(kPreallocatedCount);
+}
+
+void ExecutionBurstController::MemoryCache::setBurstContext(sp<IBurstContext> burstContext) {
+    std::lock_guard guard(mMutex);
+    mBurstContext = std::move(burstContext);
+}
+
+std::pair<int32_t, ExecutionBurstController::MemoryCache::SharedCleanup>
+ExecutionBurstController::MemoryCache::cacheMemory(const nn::SharedMemory& memory) {
+    std::unique_lock lock(mMutex);
+    base::ScopedLockAssertion lockAssert(mMutex);
+
+    // Use existing cache entry if (1) the Memory object is in the cache and (2) the cache entry is
+    // not currently being freed.
+    auto iter = mMemoryIdToSlot.find(memory);
+    while (iter != mMemoryIdToSlot.end()) {
+        const int32_t slot = iter->second;
+        if (auto cleaner = mCacheCleaner.at(slot).lock()) {
+            return std::make_pair(slot, std::move(cleaner));
+        }
+
+        // If the code reaches this point, the Memory object was in the cache, but is currently
+        // being destroyed. This code waits until the cache entry has been freed, then loops to
+        // ensure the cache entry has been freed or has been made present by another thread.
+        mCond.wait(lock);
+        iter = mMemoryIdToSlot.find(memory);
+    }
+
+    // Allocate a new cache entry.
+    const int32_t slot = allocateSlotLocked();
+    mMemoryIdToSlot[memory] = slot;
+    mMemoryCache[slot] = memory;
+
+    // Create reference-counted self-cleaning cache object.
+    auto self = weak_from_this();
+    Task cleanup = [memory, memoryCache = std::move(self)] {
+        if (const auto lock = memoryCache.lock()) {
+            lock->freeMemory(memory);
+        }
+    };
+    auto cleaner = std::make_shared<const Cleanup>(std::move(cleanup));
+    mCacheCleaner[slot] = cleaner;
+
+    return std::make_pair(slot, std::move(cleaner));
+}
+
+nn::GeneralResult<nn::SharedMemory> ExecutionBurstController::MemoryCache::getMemory(int32_t slot) {
+    std::lock_guard guard(mMutex);
+    if (slot < 0 || static_cast<size_t>(slot) >= mMemoryCache.size()) {
+        return NN_ERROR() << "Invalid slot: " << slot << " vs " << mMemoryCache.size();
+    }
+    return mMemoryCache[slot];
+}
+
+void ExecutionBurstController::MemoryCache::freeMemory(const nn::SharedMemory& memory) {
+    {
+        std::lock_guard guard(mMutex);
+        const int32_t slot = mMemoryIdToSlot.at(memory);
+        if (mBurstContext) {
+            mBurstContext->freeMemory(slot);
+        }
+        mMemoryIdToSlot.erase(memory);
+        mMemoryCache[slot] = {};
+        mCacheCleaner[slot].reset();
+        mFreeSlots.push(slot);
+    }
+    mCond.notify_all();
+}
+
+int32_t ExecutionBurstController::MemoryCache::allocateSlotLocked() {
+    constexpr size_t kMaxNumberOfSlots = std::numeric_limits<int32_t>::max();
+
+    // If there is a free slot, use it.
+    if (!mFreeSlots.empty()) {
+        const int32_t slot = mFreeSlots.top();
+        mFreeSlots.pop();
+        return slot;
+    }
+
+    // Use a slot for the first time.
+    CHECK_LT(mMemoryCache.size(), kMaxNumberOfSlots) << "Exceeded maximum number of slots!";
+    const int32_t slot = static_cast<int32_t>(mMemoryCache.size());
+    mMemoryCache.emplace_back();
+    mCacheCleaner.emplace_back();
+
+    return slot;
+}
+
+// ExecutionBurstCallback methods
+
+ExecutionBurstController::ExecutionBurstCallback::ExecutionBurstCallback(
+        const std::shared_ptr<MemoryCache>& memoryCache)
+    : kMemoryCache(memoryCache) {
+    CHECK(memoryCache != nullptr);
+}
+
+Return<void> ExecutionBurstController::ExecutionBurstCallback::getMemories(
+        const hidl_vec<int32_t>& slots, getMemories_cb cb) {
+    const auto memoryCache = kMemoryCache.lock();
+    if (memoryCache == nullptr) {
+        LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories called after "
+                      "the MemoryCache has been freed";
+        cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+        return Void();
+    }
+
+    const auto maybeMemories = getMemoriesHelper(slots, memoryCache);
+    if (!maybeMemories.has_value()) {
+        const auto& [message, code] = maybeMemories.error();
+        LOG(ERROR) << "ExecutionBurstController::ExecutionBurstCallback::getMemories failed with "
+                   << code << ": " << message;
+        cb(V1_0::ErrorStatus::INVALID_ARGUMENT, {});
+        return Void();
+    }
+
+    cb(V1_0::ErrorStatus::NONE, maybeMemories.value());
+    return Void();
+}
+
+// ExecutionBurstController methods
+
+nn::GeneralResult<std::shared_ptr<const ExecutionBurstController>> ExecutionBurstController::create(
+        nn::SharedPreparedModel preparedModel, const sp<V1_2::IPreparedModel>& hidlPreparedModel,
+        std::chrono::microseconds pollingTimeWindow) {
+    // check inputs
+    if (preparedModel == nullptr || hidlPreparedModel == nullptr) {
+        return NN_ERROR() << "ExecutionBurstController::create passed a nullptr";
+    }
+
+    // create FMQ objects
+    auto [requestChannelSender, requestChannelDescriptor] =
+            NN_TRY(RequestChannelSender::create(kExecutionBurstChannelLength));
+    auto [resultChannelReceiver, resultChannelDescriptor] =
+            NN_TRY(ResultChannelReceiver::create(kExecutionBurstChannelLength, pollingTimeWindow));
+
+    // check FMQ objects
+    CHECK(requestChannelSender != nullptr);
+    CHECK(requestChannelDescriptor != nullptr);
+    CHECK(resultChannelReceiver != nullptr);
+    CHECK(resultChannelDescriptor != nullptr);
+
+    // create memory cache
+    auto memoryCache = std::make_shared<MemoryCache>();
+
+    // create callback object
+    auto burstCallback = sp<ExecutionBurstCallback>::make(memoryCache);
+    auto cb = hal::utils::CallbackValue(executionBurstResultCallback);
+
+    // configure burst
+    const Return<void> ret = hidlPreparedModel->configureExecutionBurst(
+            burstCallback, *requestChannelDescriptor, *resultChannelDescriptor, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    auto burstContext = NN_TRY(cb.take());
+    memoryCache->setBurstContext(burstContext);
+
+    // create death handler object
+    auto deathHandler = NN_TRY(neuralnetworks::utils::DeathHandler::create(burstContext));
+    deathHandler.protectCallbackForLifetimeOfDeathHandler(requestChannelSender.get());
+    deathHandler.protectCallbackForLifetimeOfDeathHandler(resultChannelReceiver.get());
+
+    // make and return controller
+    return std::make_shared<const ExecutionBurstController>(
+            PrivateConstructorTag{}, std::move(preparedModel), std::move(requestChannelSender),
+            std::move(resultChannelReceiver), std::move(burstCallback), std::move(burstContext),
+            std::move(memoryCache), std::move(deathHandler));
+}
+
+ExecutionBurstController::ExecutionBurstController(
+        PrivateConstructorTag /*tag*/, nn::SharedPreparedModel preparedModel,
+        std::unique_ptr<RequestChannelSender> requestChannelSender,
+        std::unique_ptr<ResultChannelReceiver> resultChannelReceiver,
+        sp<ExecutionBurstCallback> callback, sp<IBurstContext> burstContext,
+        std::shared_ptr<MemoryCache> memoryCache, neuralnetworks::utils::DeathHandler deathHandler)
+    : kPreparedModel(std::move(preparedModel)),
+      mRequestChannelSender(std::move(requestChannelSender)),
+      mResultChannelReceiver(std::move(resultChannelReceiver)),
+      mBurstCallback(std::move(callback)),
+      mBurstContext(std::move(burstContext)),
+      mMemoryCache(std::move(memoryCache)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+ExecutionBurstController::OptionalCacheHold ExecutionBurstController::cacheMemory(
+        const nn::SharedMemory& memory) const {
+    auto [slot, hold] = mMemoryCache->cacheMemory(memory);
+    return hold;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ExecutionBurstController::execute(const nn::Request& request, nn::MeasureTiming measure,
+                                  const nn::OptionalTimePoint& deadline,
+                                  const nn::OptionalDuration& loopTimeoutDuration) const {
+    // This is the first point when we know an execution is occurring, so begin to collect
+    // systraces. Note that the first point we can begin collecting systraces in
+    // ExecutionBurstServer is when the RequestChannelReceiver realizes there is data in the FMQ, so
+    // ExecutionBurstServer collects systraces at different points in the code.
+    NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::execute");
+
+    // if the request is valid but of a higher version than what's supported in burst execution,
+    // fall back to another execution path
+    if (const auto version = NN_TRY(hal::utils::makeExecutionFailure(nn::validate(request)));
+        version > nn::Version::ANDROID_Q) {
+        // fallback to another execution path if the packet could not be sent
+        return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+    }
+
+    // ensure that request is ready for IPC
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    // clear pools field of request, as they will be provided via slots
+    const auto requestWithoutPools = nn::Request{
+            .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
+    auto hidlRequest = NN_TRY(
+            hal::utils::makeExecutionFailure(V1_0::utils::unvalidatedConvert(requestWithoutPools)));
+    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+
+    std::vector<int32_t> slots;
+    std::vector<OptionalCacheHold> holds;
+    slots.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+        slots.push_back(slot);
+        holds.push_back(std::move(hold));
+    }
+
+    // send request packet
+    const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+    const auto fallback = [this, &request, measure, &deadline, &loopTimeoutDuration] {
+        return kPreparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    return executeInternal(requestPacket, relocation, fallback);
+}
+
+// See IBurst::createReusableExecution for information on this method.
+nn::GeneralResult<nn::SharedExecution> ExecutionBurstController::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    NNTRACE_RT(NNTRACE_PHASE_EXECUTION, "ExecutionBurstController::createReusableExecution");
+
+    // if the request is valid but of a higher version than what's supported in burst execution,
+    // fall back to another execution path
+    if (const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(request)));
+        version > nn::Version::ANDROID_Q) {
+        // fallback to another execution path if the packet could not be sent
+        return kPreparedModel->createReusableExecution(request, measure, loopTimeoutDuration);
+    }
+
+    // ensure that request is ready for IPC
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    // clear pools field of request, as they will be provided via slots
+    const auto requestWithoutPools = nn::Request{
+            .inputs = requestInShared.inputs, .outputs = requestInShared.outputs, .pools = {}};
+    auto hidlRequest = NN_TRY(V1_0::utils::unvalidatedConvert(requestWithoutPools));
+    const auto hidlMeasure = NN_TRY(convert(measure));
+
+    std::vector<int32_t> slots;
+    std::vector<OptionalCacheHold> holds;
+    slots.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        auto [slot, hold] = mMemoryCache->cacheMemory(std::get<nn::SharedMemory>(memoryPool));
+        slots.push_back(slot);
+        holds.push_back(std::move(hold));
+    }
+
+    const auto requestPacket = serialize(hidlRequest, hidlMeasure, slots);
+    return BurstExecution::create(shared_from_this(), std::move(requestPacket),
+                                  std::move(relocation), std::move(holds));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ExecutionBurstController::executeInternal(const std::vector<FmqRequestDatum>& requestPacket,
+                                          const hal::utils::RequestRelocation& relocation,
+                                          FallbackFunction fallback) const {
+    NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+                 "ExecutionBurstController::executeInternal");
+
+    // Ensure that at most one execution is in flight at any given time.
+    const bool alreadyInFlight = mExecutionInFlight.test_and_set();
+    if (alreadyInFlight) {
+        return NN_ERROR() << "IBurst already has an execution in flight";
+    }
+    const auto guard = base::make_scope_guard([this] { mExecutionInFlight.clear(); });
+
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    // send request packet
+    const auto sendStatus = mRequestChannelSender->sendPacket(requestPacket);
+    if (!sendStatus.ok()) {
+        // fallback to another execution path if the packet could not be sent
+        if (fallback) {
+            return fallback();
+        }
+        return NN_ERROR() << "Error sending FMQ packet: " << sendStatus.error();
+    }
+
+    // get result packet
+    const auto [status, outputShapes, timing] =
+            NN_TRY(hal::utils::makeExecutionFailure(mResultChannelReceiver->getBlocking()));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return executionCallback(status, outputShapes, timing);
+}
+
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+        std::shared_ptr<const ExecutionBurstController> controller,
+        std::vector<FmqRequestDatum> request, hal::utils::RequestRelocation relocation,
+        std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds) {
+    if (controller == nullptr) {
+        return NN_ERROR() << "V1_2::utils::BurstExecution::create must have non-null controller";
+    }
+
+    return std::make_shared<const BurstExecution>(PrivateConstructorTag{}, std::move(controller),
+                                                  std::move(request), std::move(relocation),
+                                                  std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/,
+                               std::shared_ptr<const ExecutionBurstController> controller,
+                               std::vector<FmqRequestDatum> request,
+                               hal::utils::RequestRelocation relocation,
+                               std::vector<ExecutionBurstController::OptionalCacheHold> cacheHolds)
+    : kController(std::move(controller)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return kController->executeInternal(kRequest, kRelocation, /*fallback=*/nullptr);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on burst object";
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
new file mode 100644
index 0000000..c67159e
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstServer.cpp
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "ExecutionBurstServer"
+
+#include "ExecutionBurstServer.h"
+#include "Conversions.h"
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/logging.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <algorithm>
+#include <cstring>
+#include <limits>
+#include <map>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "Tracing.h"
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using neuralnetworks::utils::makeExecutionFailure;
+
+constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
+                                    std::numeric_limits<uint64_t>::max()};
+
+nn::GeneralResult<std::vector<nn::SharedMemory>> getMemoriesCallback(
+        V1_0::ErrorStatus status, const hidl_vec<hidl_memory>& memories) {
+    HANDLE_HAL_STATUS(status) << "getting burst memories failed with " << toString(status);
+    std::vector<nn::SharedMemory> canonicalMemories;
+    canonicalMemories.reserve(memories.size());
+    for (const auto& memory : memories) {
+        canonicalMemories.push_back(NN_TRY(nn::convert(memory)));
+    }
+    return canonicalMemories;
+}
+
+}  // anonymous namespace
+
+ExecutionBurstServer::MemoryCache::MemoryCache(nn::SharedBurst burstExecutor,
+                                               sp<IBurstCallback> burstCallback)
+    : kBurstExecutor(std::move(burstExecutor)), kBurstCallback(std::move(burstCallback)) {
+    CHECK(kBurstExecutor != nullptr);
+    CHECK(kBurstCallback != nullptr);
+}
+
+nn::GeneralResult<std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>>
+ExecutionBurstServer::MemoryCache::getCacheEntries(const std::vector<int32_t>& slots) {
+    std::lock_guard guard(mMutex);
+    NN_TRY(ensureCacheEntriesArePresentLocked(slots));
+
+    std::vector<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>> results;
+    results.reserve(slots.size());
+    for (int32_t slot : slots) {
+        results.push_back(NN_TRY(getCacheEntryLocked(slot)));
+    }
+
+    return results;
+}
+
+nn::GeneralResult<void> ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked(
+        const std::vector<int32_t>& slots) {
+    const auto slotIsKnown = [this](int32_t slot)
+                                     REQUIRES(mMutex) { return mCache.count(slot) > 0; };
+
+    // find unique unknown slots
+    std::vector<int32_t> unknownSlots = slots;
+    std::sort(unknownSlots.begin(), unknownSlots.end());
+    auto unknownSlotsEnd = std::unique(unknownSlots.begin(), unknownSlots.end());
+    unknownSlotsEnd = std::remove_if(unknownSlots.begin(), unknownSlotsEnd, slotIsKnown);
+    unknownSlots.erase(unknownSlotsEnd, unknownSlots.end());
+
+    // quick-exit if all slots are known
+    if (unknownSlots.empty()) {
+        return {};
+    }
+
+    auto cb = neuralnetworks::utils::CallbackValue(getMemoriesCallback);
+
+    const auto ret = kBurstCallback->getMemories(unknownSlots, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    auto returnedMemories = NN_TRY(cb.take());
+
+    if (returnedMemories.size() != unknownSlots.size()) {
+        return NN_ERROR()
+               << "ExecutionBurstServer::MemoryCache::ensureCacheEntriesArePresentLocked: Error "
+                  "retrieving memories -- count mismatch between requested memories ("
+               << unknownSlots.size() << ") and returned memories (" << returnedMemories.size()
+               << ")";
+    }
+
+    // add memories to unknown slots
+    for (size_t i = 0; i < unknownSlots.size(); ++i) {
+        addCacheEntryLocked(unknownSlots[i], std::move(returnedMemories[i]));
+    }
+
+    return {};
+}
+
+nn::GeneralResult<std::pair<nn::SharedMemory, nn::IBurst::OptionalCacheHold>>
+ExecutionBurstServer::MemoryCache::getCacheEntryLocked(int32_t slot) {
+    if (const auto iter = mCache.find(slot); iter != mCache.end()) {
+        return iter->second;
+    }
+    return NN_ERROR()
+           << "ExecutionBurstServer::MemoryCache::getCacheEntryLocked failed because slot " << slot
+           << " is not present in the cache";
+}
+
+void ExecutionBurstServer::MemoryCache::addCacheEntryLocked(int32_t slot, nn::SharedMemory memory) {
+    auto hold = kBurstExecutor->cacheMemory(memory);
+    mCache.emplace(slot, std::make_pair(std::move(memory), std::move(hold)));
+}
+
+void ExecutionBurstServer::MemoryCache::removeCacheEntry(int32_t slot) {
+    std::lock_guard guard(mMutex);
+    mCache.erase(slot);
+}
+
+// ExecutionBurstServer methods
+
+nn::GeneralResult<sp<ExecutionBurstServer>> ExecutionBurstServer::create(
+        const sp<IBurstCallback>& callback, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+        const MQDescriptorSync<FmqResultDatum>& resultChannel, nn::SharedBurst burstExecutor,
+        std::chrono::microseconds pollingTimeWindow) {
+    // check inputs
+    if (callback == nullptr || burstExecutor == nullptr) {
+        return NN_ERROR() << "ExecutionBurstServer::create passed a nullptr";
+    }
+
+    // create FMQ objects
+    auto requestChannelReceiver =
+            NN_TRY(RequestChannelReceiver::create(requestChannel, pollingTimeWindow));
+    auto resultChannelSender = NN_TRY(ResultChannelSender::create(resultChannel));
+
+    // check FMQ objects
+    CHECK(requestChannelReceiver != nullptr);
+    CHECK(resultChannelSender != nullptr);
+
+    // make and return context
+    return sp<ExecutionBurstServer>::make(PrivateConstructorTag{}, callback,
+                                          std::move(requestChannelReceiver),
+                                          std::move(resultChannelSender), std::move(burstExecutor));
+}
+
+ExecutionBurstServer::ExecutionBurstServer(PrivateConstructorTag /*tag*/,
+                                           const sp<IBurstCallback>& callback,
+                                           std::unique_ptr<RequestChannelReceiver> requestChannel,
+                                           std::unique_ptr<ResultChannelSender> resultChannel,
+                                           nn::SharedBurst burstExecutor)
+    : mCallback(callback),
+      mRequestChannelReceiver(std::move(requestChannel)),
+      mResultChannelSender(std::move(resultChannel)),
+      mBurstExecutor(std::move(burstExecutor)),
+      mMemoryCache(mBurstExecutor, mCallback) {
+    // TODO: highly document the threading behavior of this class
+    mWorker = std::thread([this] { task(); });
+}
+
+ExecutionBurstServer::~ExecutionBurstServer() {
+    // set teardown flag
+    mTeardown = true;
+    mRequestChannelReceiver->invalidate();
+
+    // wait for task thread to end
+    mWorker.join();
+}
+
+Return<void> ExecutionBurstServer::freeMemory(int32_t slot) {
+    mMemoryCache.removeCacheEntry(slot);
+    return Void();
+}
+
+void ExecutionBurstServer::task() {
+    // loop until the burst object is being destroyed
+    while (!mTeardown) {
+        // receive request
+        auto arguments = mRequestChannelReceiver->getBlocking();
+
+        // if the request packet was not properly received, return a generic error and skip the
+        // execution
+        //
+        // if the burst is being torn down, skip the execution so the "task" function can end
+        if (!arguments.has_value()) {
+            if (!mTeardown) {
+                mResultChannelSender->send(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+            }
+            continue;
+        }
+
+        // unpack the arguments; types are Request, std::vector<int32_t>, and MeasureTiming,
+        // respectively
+        const auto [requestWithoutPools, slotsOfPools, measure] = std::move(arguments).value();
+
+        auto result = execute(requestWithoutPools, slotsOfPools, measure);
+
+        // return result
+        if (result.has_value()) {
+            const auto& [outputShapes, timing] = result.value();
+            mResultChannelSender->send(V1_0::ErrorStatus::NONE, outputShapes, timing);
+        } else {
+            const auto& [message, code, outputShapes] = result.error();
+            LOG(ERROR) << "IBurst::execute failed with " << code << ": " << message;
+            mResultChannelSender->send(convert(code).value(), convert(outputShapes).value(),
+                                       kNoTiming);
+        }
+    }
+}
+
+nn::ExecutionResult<std::pair<hidl_vec<OutputShape>, Timing>> ExecutionBurstServer::execute(
+        const V1_0::Request& requestWithoutPools, const std::vector<int32_t>& slotsOfPools,
+        MeasureTiming measure) {
+    NNTRACE_FULL(NNTRACE_LAYER_IPC, NNTRACE_PHASE_EXECUTION,
+                 "ExecutionBurstServer getting memory, executing, and returning results");
+
+    // ensure executor with cache has required memory
+    const auto cacheEntries =
+            NN_TRY(makeExecutionFailure(mMemoryCache.getCacheEntries(slotsOfPools)));
+
+    // convert request, populating its pools
+    // This code performs an unvalidated convert because the request object without its pools is
+    // invalid because it is incomplete. Instead, the validation is performed after the memory pools
+    // have been added to the request.
+    auto canonicalRequest =
+            NN_TRY(makeExecutionFailure(nn::unvalidatedConvert(requestWithoutPools)));
+    CHECK(canonicalRequest.pools.empty());
+    std::transform(cacheEntries.begin(), cacheEntries.end(),
+                   std::back_inserter(canonicalRequest.pools),
+                   [](const auto& cacheEntry) { return cacheEntry.first; });
+    NN_TRY(makeExecutionFailure(validate(canonicalRequest)));
+
+    nn::MeasureTiming canonicalMeasure = NN_TRY(makeExecutionFailure(nn::convert(measure)));
+
+    const auto [outputShapes, timing] =
+            NN_TRY(mBurstExecutor->execute(canonicalRequest, canonicalMeasure, {}, {}));
+
+    return std::make_pair(NN_TRY(makeExecutionFailure(convert(outputShapes))),
+                          NN_TRY(makeExecutionFailure(convert(timing))));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
new file mode 100644
index 0000000..1bdde1e
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/ExecutionBurstUtils.cpp
@@ -0,0 +1,704 @@
+/*
+ * Copyright (C) 2019 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.
+ */
+
+#define LOG_TAG "ExecutionBurstUtils"
+
+#include "ExecutionBurstUtils.h"
+
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <fmq/MessageQueue.h>
+#include <hidl/MQDescriptor.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <atomic>
+#include <chrono>
+#include <memory>
+#include <thread>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+constexpr V1_2::Timing kNoTiming = {std::numeric_limits<uint64_t>::max(),
+                                    std::numeric_limits<uint64_t>::max()};
+
+std::chrono::microseconds getPollingTimeWindow(const std::string& property) {
+    constexpr int32_t kDefaultPollingTimeWindow = 0;
+#ifdef NN_DEBUGGABLE
+    constexpr int32_t kMinPollingTimeWindow = 0;
+    const int32_t selectedPollingTimeWindow =
+            base::GetIntProperty(property, kDefaultPollingTimeWindow, kMinPollingTimeWindow);
+    return std::chrono::microseconds(selectedPollingTimeWindow);
+#else
+    (void)property;
+    return std::chrono::microseconds(kDefaultPollingTimeWindow);
+#endif  // NN_DEBUGGABLE
+}
+
+}  // namespace
+
+std::chrono::microseconds getBurstControllerPollingTimeWindow() {
+    return getPollingTimeWindow("debug.nn.burst-controller-polling-window");
+}
+
+std::chrono::microseconds getBurstServerPollingTimeWindow() {
+    return getPollingTimeWindow("debug.nn.burst-server-polling-window");
+}
+
+// serialize a request into a packet
+std::vector<FmqRequestDatum> serialize(const V1_0::Request& request, V1_2::MeasureTiming measure,
+                                       const std::vector<int32_t>& slots) {
+    // count how many elements need to be sent for a request
+    size_t count = 2 + request.inputs.size() + request.outputs.size() + slots.size();
+    for (const auto& input : request.inputs) {
+        count += input.dimensions.size();
+    }
+    for (const auto& output : request.outputs) {
+        count += output.dimensions.size();
+    }
+    CHECK_LE(count, std::numeric_limits<uint32_t>::max());
+
+    // create buffer to temporarily store elements
+    std::vector<FmqRequestDatum> data;
+    data.reserve(count);
+
+    // package packetInfo
+    data.emplace_back();
+    data.back().packetInformation(
+            {.packetSize = static_cast<uint32_t>(count),
+             .numberOfInputOperands = static_cast<uint32_t>(request.inputs.size()),
+             .numberOfOutputOperands = static_cast<uint32_t>(request.outputs.size()),
+             .numberOfPools = static_cast<uint32_t>(slots.size())});
+
+    // package input data
+    for (const auto& input : request.inputs) {
+        // package operand information
+        data.emplace_back();
+        data.back().inputOperandInformation(
+                {.hasNoValue = input.hasNoValue,
+                 .location = input.location,
+                 .numberOfDimensions = static_cast<uint32_t>(input.dimensions.size())});
+
+        // package operand dimensions
+        for (uint32_t dimension : input.dimensions) {
+            data.emplace_back();
+            data.back().inputOperandDimensionValue(dimension);
+        }
+    }
+
+    // package output data
+    for (const auto& output : request.outputs) {
+        // package operand information
+        data.emplace_back();
+        data.back().outputOperandInformation(
+                {.hasNoValue = output.hasNoValue,
+                 .location = output.location,
+                 .numberOfDimensions = static_cast<uint32_t>(output.dimensions.size())});
+
+        // package operand dimensions
+        for (uint32_t dimension : output.dimensions) {
+            data.emplace_back();
+            data.back().outputOperandDimensionValue(dimension);
+        }
+    }
+
+    // package pool identifier
+    for (int32_t slot : slots) {
+        data.emplace_back();
+        data.back().poolIdentifier(slot);
+    }
+
+    // package measureTiming
+    data.emplace_back();
+    data.back().measureTiming(measure);
+
+    CHECK_EQ(data.size(), count);
+
+    // return packet
+    return data;
+}
+
+// serialize result
+std::vector<FmqResultDatum> serialize(V1_0::ErrorStatus errorStatus,
+                                      const std::vector<V1_2::OutputShape>& outputShapes,
+                                      V1_2::Timing timing) {
+    // count how many elements need to be sent for a request
+    size_t count = 2 + outputShapes.size();
+    for (const auto& outputShape : outputShapes) {
+        count += outputShape.dimensions.size();
+    }
+
+    // create buffer to temporarily store elements
+    std::vector<FmqResultDatum> data;
+    data.reserve(count);
+
+    // package packetInfo
+    data.emplace_back();
+    data.back().packetInformation({.packetSize = static_cast<uint32_t>(count),
+                                   .errorStatus = errorStatus,
+                                   .numberOfOperands = static_cast<uint32_t>(outputShapes.size())});
+
+    // package output shape data
+    for (const auto& operand : outputShapes) {
+        // package operand information
+        data.emplace_back();
+        data.back().operandInformation(
+                {.isSufficient = operand.isSufficient,
+                 .numberOfDimensions = static_cast<uint32_t>(operand.dimensions.size())});
+
+        // package operand dimensions
+        for (uint32_t dimension : operand.dimensions) {
+            data.emplace_back();
+            data.back().operandDimensionValue(dimension);
+        }
+    }
+
+    // package executionTiming
+    data.emplace_back();
+    data.back().executionTiming(timing);
+
+    CHECK_EQ(data.size(), count);
+
+    // return result
+    return data;
+}
+
+// deserialize request
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>> deserialize(
+        const std::vector<FmqRequestDatum>& data) {
+    using discriminator = FmqRequestDatum::hidl_discriminator;
+
+    size_t index = 0;
+
+    // validate packet information
+    if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+        return NN_ERROR() << "FMQ Request packet ill-formed";
+    }
+
+    // unpackage packet information
+    const FmqRequestDatum::PacketInformation& packetInfo = data[index].packetInformation();
+    index++;
+    const uint32_t packetSize = packetInfo.packetSize;
+    const uint32_t numberOfInputOperands = packetInfo.numberOfInputOperands;
+    const uint32_t numberOfOutputOperands = packetInfo.numberOfOutputOperands;
+    const uint32_t numberOfPools = packetInfo.numberOfPools;
+
+    // verify packet size
+    if (data.size() != packetSize) {
+        return NN_ERROR() << "FMQ Request packet ill-formed";
+    }
+
+    // unpackage input operands
+    std::vector<V1_0::RequestArgument> inputs;
+    inputs.reserve(numberOfInputOperands);
+    for (size_t operand = 0; operand < numberOfInputOperands; ++operand) {
+        // validate input operand information
+        if (data[index].getDiscriminator() != discriminator::inputOperandInformation) {
+            return NN_ERROR() << "FMQ Request packet ill-formed";
+        }
+
+        // unpackage operand information
+        const FmqRequestDatum::OperandInformation& operandInfo =
+                data[index].inputOperandInformation();
+        index++;
+        const bool hasNoValue = operandInfo.hasNoValue;
+        const V1_0::DataLocation location = operandInfo.location;
+        const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+        // unpackage operand dimensions
+        std::vector<uint32_t> dimensions;
+        dimensions.reserve(numberOfDimensions);
+        for (size_t i = 0; i < numberOfDimensions; ++i) {
+            // validate dimension
+            if (data[index].getDiscriminator() != discriminator::inputOperandDimensionValue) {
+                return NN_ERROR() << "FMQ Request packet ill-formed";
+            }
+
+            // unpackage dimension
+            const uint32_t dimension = data[index].inputOperandDimensionValue();
+            index++;
+
+            // store result
+            dimensions.push_back(dimension);
+        }
+
+        // store result
+        inputs.push_back(
+                {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
+    }
+
+    // unpackage output operands
+    std::vector<V1_0::RequestArgument> outputs;
+    outputs.reserve(numberOfOutputOperands);
+    for (size_t operand = 0; operand < numberOfOutputOperands; ++operand) {
+        // validate output operand information
+        if (data[index].getDiscriminator() != discriminator::outputOperandInformation) {
+            return NN_ERROR() << "FMQ Request packet ill-formed";
+        }
+
+        // unpackage operand information
+        const FmqRequestDatum::OperandInformation& operandInfo =
+                data[index].outputOperandInformation();
+        index++;
+        const bool hasNoValue = operandInfo.hasNoValue;
+        const V1_0::DataLocation location = operandInfo.location;
+        const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+        // unpackage operand dimensions
+        std::vector<uint32_t> dimensions;
+        dimensions.reserve(numberOfDimensions);
+        for (size_t i = 0; i < numberOfDimensions; ++i) {
+            // validate dimension
+            if (data[index].getDiscriminator() != discriminator::outputOperandDimensionValue) {
+                return NN_ERROR() << "FMQ Request packet ill-formed";
+            }
+
+            // unpackage dimension
+            const uint32_t dimension = data[index].outputOperandDimensionValue();
+            index++;
+
+            // store result
+            dimensions.push_back(dimension);
+        }
+
+        // store result
+        outputs.push_back(
+                {.hasNoValue = hasNoValue, .location = location, .dimensions = dimensions});
+    }
+
+    // unpackage pools
+    std::vector<int32_t> slots;
+    slots.reserve(numberOfPools);
+    for (size_t pool = 0; pool < numberOfPools; ++pool) {
+        // validate input operand information
+        if (data[index].getDiscriminator() != discriminator::poolIdentifier) {
+            return NN_ERROR() << "FMQ Request packet ill-formed";
+        }
+
+        // unpackage operand information
+        const int32_t poolId = data[index].poolIdentifier();
+        index++;
+
+        // store result
+        slots.push_back(poolId);
+    }
+
+    // validate measureTiming
+    if (data[index].getDiscriminator() != discriminator::measureTiming) {
+        return NN_ERROR() << "FMQ Request packet ill-formed";
+    }
+
+    // unpackage measureTiming
+    const V1_2::MeasureTiming measure = data[index].measureTiming();
+    index++;
+
+    // validate packet information
+    if (index != packetSize) {
+        return NN_ERROR() << "FMQ Result packet ill-formed";
+    }
+
+    // return request
+    V1_0::Request request = {.inputs = inputs, .outputs = outputs, .pools = {}};
+    return std::make_tuple(std::move(request), std::move(slots), measure);
+}
+
+// deserialize a packet into the result
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>> deserialize(
+        const std::vector<FmqResultDatum>& data) {
+    using discriminator = FmqResultDatum::hidl_discriminator;
+    size_t index = 0;
+
+    // validate packet information
+    if (data.size() == 0 || data[index].getDiscriminator() != discriminator::packetInformation) {
+        return NN_ERROR() << "FMQ Result packet ill-formed";
+    }
+
+    // unpackage packet information
+    const FmqResultDatum::PacketInformation& packetInfo = data[index].packetInformation();
+    index++;
+    const uint32_t packetSize = packetInfo.packetSize;
+    const V1_0::ErrorStatus errorStatus = packetInfo.errorStatus;
+    const uint32_t numberOfOperands = packetInfo.numberOfOperands;
+
+    // verify packet size
+    if (data.size() != packetSize) {
+        return NN_ERROR() << "FMQ Result packet ill-formed";
+    }
+
+    // unpackage operands
+    std::vector<V1_2::OutputShape> outputShapes;
+    outputShapes.reserve(numberOfOperands);
+    for (size_t operand = 0; operand < numberOfOperands; ++operand) {
+        // validate operand information
+        if (data[index].getDiscriminator() != discriminator::operandInformation) {
+            return NN_ERROR() << "FMQ Result packet ill-formed";
+        }
+
+        // unpackage operand information
+        const FmqResultDatum::OperandInformation& operandInfo = data[index].operandInformation();
+        index++;
+        const bool isSufficient = operandInfo.isSufficient;
+        const uint32_t numberOfDimensions = operandInfo.numberOfDimensions;
+
+        // unpackage operand dimensions
+        std::vector<uint32_t> dimensions;
+        dimensions.reserve(numberOfDimensions);
+        for (size_t i = 0; i < numberOfDimensions; ++i) {
+            // validate dimension
+            if (data[index].getDiscriminator() != discriminator::operandDimensionValue) {
+                return NN_ERROR() << "FMQ Result packet ill-formed";
+            }
+
+            // unpackage dimension
+            const uint32_t dimension = data[index].operandDimensionValue();
+            index++;
+
+            // store result
+            dimensions.push_back(dimension);
+        }
+
+        // store result
+        outputShapes.push_back({.dimensions = dimensions, .isSufficient = isSufficient});
+    }
+
+    // validate execution timing
+    if (data[index].getDiscriminator() != discriminator::executionTiming) {
+        return NN_ERROR() << "FMQ Result packet ill-formed";
+    }
+
+    // unpackage execution timing
+    const V1_2::Timing timing = data[index].executionTiming();
+    index++;
+
+    // validate packet information
+    if (index != packetSize) {
+        return NN_ERROR() << "FMQ Result packet ill-formed";
+    }
+
+    // return result
+    return std::make_tuple(errorStatus, std::move(outputShapes), timing);
+}
+
+// RequestChannelSender methods
+
+nn::GeneralResult<
+        std::pair<std::unique_ptr<RequestChannelSender>, const MQDescriptorSync<FmqRequestDatum>*>>
+RequestChannelSender::create(size_t channelLength) {
+    auto requestChannelSender =
+            std::make_unique<RequestChannelSender>(PrivateConstructorTag{}, channelLength);
+    if (!requestChannelSender->mFmqRequestChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelSender";
+    }
+
+    const MQDescriptorSync<FmqRequestDatum>* descriptor =
+            requestChannelSender->mFmqRequestChannel.getDesc();
+    return std::make_pair(std::move(requestChannelSender), descriptor);
+}
+
+RequestChannelSender::RequestChannelSender(PrivateConstructorTag /*tag*/, size_t channelLength)
+    : mFmqRequestChannel(channelLength, /*configureEventFlagWord=*/true) {}
+
+nn::Result<void> RequestChannelSender::send(const V1_0::Request& request,
+                                            V1_2::MeasureTiming measure,
+                                            const std::vector<int32_t>& slots) {
+    const std::vector<FmqRequestDatum> serialized = serialize(request, measure, slots);
+    return sendPacket(serialized);
+}
+
+nn::Result<void> RequestChannelSender::sendPacket(const std::vector<FmqRequestDatum>& packet) {
+    if (!mValid) {
+        return NN_ERROR() << "FMQ object is invalid";
+    }
+
+    if (packet.size() > mFmqRequestChannel.availableToWrite()) {
+        return NN_ERROR()
+               << "RequestChannelSender::sendPacket -- packet size exceeds size available in FMQ";
+    }
+
+    // Always send the packet with "blocking" because this signals the futex and unblocks the
+    // consumer if it is waiting on the futex.
+    const bool success = mFmqRequestChannel.writeBlocking(packet.data(), packet.size());
+    if (!success) {
+        return NN_ERROR()
+               << "RequestChannelSender::sendPacket -- FMQ's writeBlocking returned an error";
+    }
+
+    return {};
+}
+
+void RequestChannelSender::notifyAsDeadObject() {
+    mValid = false;
+}
+
+// RequestChannelReceiver methods
+
+nn::GeneralResult<std::unique_ptr<RequestChannelReceiver>> RequestChannelReceiver::create(
+        const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+        std::chrono::microseconds pollingTimeWindow) {
+    auto requestChannelReceiver = std::make_unique<RequestChannelReceiver>(
+            PrivateConstructorTag{}, requestChannel, pollingTimeWindow);
+
+    if (!requestChannelReceiver->mFmqRequestChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelReceiver";
+    }
+    if (requestChannelReceiver->mFmqRequestChannel.getEventFlagWord() == nullptr) {
+        return NN_ERROR()
+               << "RequestChannelReceiver::create was passed an MQDescriptor without an EventFlag";
+    }
+
+    return requestChannelReceiver;
+}
+
+RequestChannelReceiver::RequestChannelReceiver(
+        PrivateConstructorTag /*tag*/, const MQDescriptorSync<FmqRequestDatum>& requestChannel,
+        std::chrono::microseconds pollingTimeWindow)
+    : mFmqRequestChannel(requestChannel), kPollingTimeWindow(pollingTimeWindow) {}
+
+nn::Result<std::tuple<V1_0::Request, std::vector<int32_t>, V1_2::MeasureTiming>>
+RequestChannelReceiver::getBlocking() {
+    const auto packet = NN_TRY(getPacketBlocking());
+    return deserialize(packet);
+}
+
+void RequestChannelReceiver::invalidate() {
+    mTeardown = true;
+
+    // force unblock
+    // ExecutionBurstServer is by default waiting on a request packet. If the client process
+    // destroys its burst object, the server may still be waiting on the futex. This force unblock
+    // wakes up any thread waiting on the futex.
+    const auto data = serialize(V1_0::Request{}, V1_2::MeasureTiming::NO, {});
+    mFmqRequestChannel.writeBlocking(data.data(), data.size());
+}
+
+nn::Result<std::vector<FmqRequestDatum>> RequestChannelReceiver::getPacketBlocking() {
+    if (mTeardown) {
+        return NN_ERROR() << "FMQ object is being torn down";
+    }
+
+    // First spend time polling if results are available in FMQ instead of waiting on the futex.
+    // Polling is more responsive (yielding lower latencies), but can take up more power, so only
+    // poll for a limited period of time.
+
+    auto& getCurrentTime = std::chrono::high_resolution_clock::now;
+    const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
+
+    while (getCurrentTime() < timeToStopPolling) {
+        // if class is being torn down, immediately return
+        if (mTeardown.load(std::memory_order_relaxed)) {
+            return NN_ERROR() << "FMQ object is being torn down";
+        }
+
+        // Check if data is available. If it is, immediately retrieve it and return.
+        const size_t available = mFmqRequestChannel.availableToRead();
+        if (available > 0) {
+            std::vector<FmqRequestDatum> packet(available);
+            const bool success = mFmqRequestChannel.readBlocking(packet.data(), available);
+            if (!success) {
+                return NN_ERROR() << "Error receiving packet";
+            }
+            return packet;
+        }
+
+        std::this_thread::yield();
+    }
+
+    // If we get to this point, we either stopped polling because it was taking too long or polling
+    // was not allowed. Instead, perform a blocking call which uses a futex to save power.
+
+    // wait for request packet and read first element of request packet
+    FmqRequestDatum datum;
+    bool success = mFmqRequestChannel.readBlocking(&datum, 1);
+
+    // retrieve remaining elements
+    // NOTE: all of the data is already available at this point, so there's no need to do a blocking
+    // wait to wait for more data. This is known because in FMQ, all writes are published (made
+    // available) atomically. Currently, the producer always publishes the entire packet in one
+    // function call, so if the first element of the packet is available, the remaining elements are
+    // also available.
+    const size_t count = mFmqRequestChannel.availableToRead();
+    std::vector<FmqRequestDatum> packet(count + 1);
+    std::memcpy(&packet.front(), &datum, sizeof(datum));
+    success &= mFmqRequestChannel.read(packet.data() + 1, count);
+
+    // terminate loop
+    if (mTeardown) {
+        return NN_ERROR() << "FMQ object is being torn down";
+    }
+
+    // ensure packet was successfully received
+    if (!success) {
+        return NN_ERROR() << "Error receiving packet";
+    }
+
+    return packet;
+}
+
+// ResultChannelSender methods
+
+nn::GeneralResult<std::unique_ptr<ResultChannelSender>> ResultChannelSender::create(
+        const MQDescriptorSync<FmqResultDatum>& resultChannel) {
+    auto resultChannelSender =
+            std::make_unique<ResultChannelSender>(PrivateConstructorTag{}, resultChannel);
+
+    if (!resultChannelSender->mFmqResultChannel.isValid()) {
+        return NN_ERROR() << "Unable to create RequestChannelSender";
+    }
+    if (resultChannelSender->mFmqResultChannel.getEventFlagWord() == nullptr) {
+        return NN_ERROR()
+               << "ResultChannelSender::create was passed an MQDescriptor without an EventFlag";
+    }
+
+    return resultChannelSender;
+}
+
+ResultChannelSender::ResultChannelSender(PrivateConstructorTag /*tag*/,
+                                         const MQDescriptorSync<FmqResultDatum>& resultChannel)
+    : mFmqResultChannel(resultChannel) {}
+
+void ResultChannelSender::send(V1_0::ErrorStatus errorStatus,
+                               const std::vector<V1_2::OutputShape>& outputShapes,
+                               V1_2::Timing timing) {
+    const std::vector<FmqResultDatum> serialized = serialize(errorStatus, outputShapes, timing);
+    sendPacket(serialized);
+}
+
+void ResultChannelSender::sendPacket(const std::vector<FmqResultDatum>& packet) {
+    if (packet.size() > mFmqResultChannel.availableToWrite()) {
+        LOG(ERROR)
+                << "ResultChannelSender::sendPacket -- packet size exceeds size available in FMQ";
+        const std::vector<FmqResultDatum> errorPacket =
+                serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+
+        // Always send the packet with "blocking" because this signals the futex and unblocks the
+        // consumer if it is waiting on the futex.
+        mFmqResultChannel.writeBlocking(errorPacket.data(), errorPacket.size());
+    } else {
+        // Always send the packet with "blocking" because this signals the futex and unblocks the
+        // consumer if it is waiting on the futex.
+        mFmqResultChannel.writeBlocking(packet.data(), packet.size());
+    }
+}
+
+// ResultChannelReceiver methods
+
+nn::GeneralResult<
+        std::pair<std::unique_ptr<ResultChannelReceiver>, const MQDescriptorSync<FmqResultDatum>*>>
+ResultChannelReceiver::create(size_t channelLength, std::chrono::microseconds pollingTimeWindow) {
+    auto resultChannelReceiver = std::make_unique<ResultChannelReceiver>(
+            PrivateConstructorTag{}, channelLength, pollingTimeWindow);
+    if (!resultChannelReceiver->mFmqResultChannel.isValid()) {
+        return NN_ERROR() << "Unable to create ResultChannelReceiver";
+    }
+
+    const MQDescriptorSync<FmqResultDatum>* descriptor =
+            resultChannelReceiver->mFmqResultChannel.getDesc();
+    return std::make_pair(std::move(resultChannelReceiver), descriptor);
+}
+
+ResultChannelReceiver::ResultChannelReceiver(PrivateConstructorTag /*tag*/, size_t channelLength,
+                                             std::chrono::microseconds pollingTimeWindow)
+    : mFmqResultChannel(channelLength, /*configureEventFlagWord=*/true),
+      kPollingTimeWindow(pollingTimeWindow) {}
+
+nn::Result<std::tuple<V1_0::ErrorStatus, std::vector<V1_2::OutputShape>, V1_2::Timing>>
+ResultChannelReceiver::getBlocking() {
+    const auto packet = NN_TRY(getPacketBlocking());
+    return deserialize(packet);
+}
+
+void ResultChannelReceiver::notifyAsDeadObject() {
+    mValid = false;
+
+    // force unblock
+    // ExecutionBurstController waits on a result packet after sending a request. If the driver
+    // containing ExecutionBurstServer crashes, the controller may be waiting on the futex. This
+    // force unblock wakes up any thread waiting on the futex.
+    const auto data = serialize(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming);
+    mFmqResultChannel.writeBlocking(data.data(), data.size());
+}
+
+nn::Result<std::vector<FmqResultDatum>> ResultChannelReceiver::getPacketBlocking() {
+    if (!mValid) {
+        return NN_ERROR() << "FMQ object is invalid";
+    }
+
+    // First spend time polling if results are available in FMQ instead of waiting on the futex.
+    // Polling is more responsive (yielding lower latencies), but can take up more power, so only
+    // poll for a limited period of time.
+
+    auto& getCurrentTime = std::chrono::high_resolution_clock::now;
+    const auto timeToStopPolling = getCurrentTime() + kPollingTimeWindow;
+
+    while (getCurrentTime() < timeToStopPolling) {
+        // if class is being torn down, immediately return
+        if (!mValid.load(std::memory_order_relaxed)) {
+            return NN_ERROR() << "FMQ object is invalid";
+        }
+
+        // Check if data is available. If it is, immediately retrieve it and return.
+        const size_t available = mFmqResultChannel.availableToRead();
+        if (available > 0) {
+            std::vector<FmqResultDatum> packet(available);
+            const bool success = mFmqResultChannel.readBlocking(packet.data(), available);
+            if (!success) {
+                return NN_ERROR() << "Error receiving packet";
+            }
+            return packet;
+        }
+
+        std::this_thread::yield();
+    }
+
+    // If we get to this point, we either stopped polling because it was taking too long or polling
+    // was not allowed. Instead, perform a blocking call which uses a futex to save power.
+
+    // wait for result packet and read first element of result packet
+    FmqResultDatum datum;
+    bool success = mFmqResultChannel.readBlocking(&datum, 1);
+
+    // retrieve remaining elements
+    // NOTE: all of the data is already available at this point, so there's no need to do a blocking
+    // wait to wait for more data. This is known because in FMQ, all writes are published (made
+    // available) atomically. Currently, the producer always publishes the entire packet in one
+    // function call, so if the first element of the packet is available, the remaining elements are
+    // also available.
+    const size_t count = mFmqResultChannel.availableToRead();
+    std::vector<FmqResultDatum> packet(count + 1);
+    std::memcpy(&packet.front(), &datum, sizeof(datum));
+    success &= mFmqResultChannel.read(packet.data() + 1, count);
+
+    if (!mValid) {
+        return NN_ERROR() << "FMQ object is invalid";
+    }
+
+    // ensure packet was successfully received
+    if (!success) {
+        return NN_ERROR() << "Error receiving packet";
+    }
+
+    return packet;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/PreparedModel.cpp b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..d0ef36e
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/PreparedModel.cpp
@@ -0,0 +1,171 @@
+/*
+ * 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 "Callbacks.h"
+#include "Conversions.h"
+#include "Execution.h"
+#include "ExecutionBurstController.h"
+#include "ExecutionBurstUtils.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <chrono>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_2::utils {
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_2::IPreparedModel> preparedModel, bool executeSynchronously) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_2::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, executeSynchronously,
+                                                 std::move(preparedModel), std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, bool executeSynchronously,
+                             sp<V1_2::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kExecuteSynchronously(executeSynchronously),
+      kPreparedModel(std::move(preparedModel)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeSynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+    auto cb = hal::utils::CallbackValue(executionCallback);
+
+    const auto ret = kPreparedModel->executeSynchronously(request, measure, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeAsynchronously(const V1_0::Request& request, MeasureTiming measure) const {
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kPreparedModel->execute_1_2(request, measure, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != V1_0::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+        HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+
+    return executeInternal(hidlRequest, hidlMeasure, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const V1_0::Request& request, MeasureTiming measure,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    auto result = kExecuteSynchronously ? executeSynchronously(request, measure)
+                                        : executeAsynchronously(request, measure);
+    auto [outputShapes, timing] = NN_TRY(std::move(result));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return std::make_pair(std::move(outputShapes), timing);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& /*request*/,
+                             const std::vector<nn::SyncFence>& /*waitFor*/,
+                             nn::MeasureTiming /*measure*/,
+                             const nn::OptionalTimePoint& /*deadline*/,
+                             const nn::OptionalDuration& /*loopTimeoutDuration*/,
+                             const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IPreparedModel::executeFenced is not supported on 1.2 HAL service";
+}
+
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    auto hidlMeasure = NN_TRY(convert(measure));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+                             hidlMeasure);
+}
+
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+    auto self = shared_from_this();
+    auto fallback = [preparedModel = std::move(self)](
+                            const nn::Request& request, nn::MeasureTiming measure,
+                            const nn::OptionalTimePoint& deadline,
+                            const nn::OptionalDuration& loopTimeoutDuration)
+            -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
+        return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    const auto pollingTimeWindow = getBurstControllerPollingTimeWindow();
+    return ExecutionBurstController::create(shared_from_this(), kPreparedModel, pollingTimeWindow);
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_2::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/src/Service.cpp b/neuralnetworks/1.2/utils/src/Service.cpp
new file mode 100644
index 0000000..110188f
--- /dev/null
+++ b/neuralnetworks/1.2/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/test/DeviceTest.cpp b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..215d44c
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/DeviceTest.cpp
@@ -0,0 +1,892 @@
+/*
+ * 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 "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const nn::Model kSimpleModel = {
+        .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+                              {.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+                 .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+                 .inputIndexes = {0},
+                 .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const sp<V1_2::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+        .execTime = std::numeric_limits<float>::max(),
+        .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+    return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+        std::apply(cb, argPack);
+        return Void();
+    };
+}
+
+sp<MockDevice> createMockDevice() {
+    const auto mockDevice = MockDevice::create();
+
+    // Setup default actions for each relevant call.
+    const auto getVersionString_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kName);
+    const auto getType_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_2::DeviceType::OTHER);
+    const auto getSupportedExtensions_ret =
+            makeCallbackReturn(V1_0::ErrorStatus::NONE, hidl_vec<V1_2::Extension>{});
+    const auto getNumberOfCacheFilesNeeded_ret = makeCallbackReturn(
+            V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+    const auto getCapabilities_ret = makeCallbackReturn(
+            V1_0::ErrorStatus::NONE,
+            V1_2::Capabilities{
+                    .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+                    .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+            });
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getVersionString(_)).WillByDefault(Invoke(getVersionString_ret));
+    ON_CALL(*mockDevice, getType(_)).WillByDefault(Invoke(getType_ret));
+    ON_CALL(*mockDevice, getSupportedExtensions(_))
+            .WillByDefault(Invoke(getSupportedExtensions_ret));
+    ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .WillByDefault(Invoke(getNumberOfCacheFilesNeeded_ret));
+    ON_CALL(*mockDevice, getCapabilities_1_2(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+    // Ensure that older calls are not used.
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(0);
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getType(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+                             const sp<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const V1_2::Model& /*model*/, V1_1::ExecutionPreference /*preference*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+                   const CacheToken& /*token*/, const sp<V1_2::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_0::ErrorStatus> {
+        cb->notify_1_2(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+auto makePreparedModelFromCacheReturn(V1_0::ErrorStatus launchStatus,
+                                      V1_0::ErrorStatus returnStatus,
+                                      const sp<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+                   const CacheToken& /*token*/, const sp<V1_2::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_0::ErrorStatus> {
+        cb->notify_1_2(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(DeviceTest, invalidName) {
+    // run test
+    const auto device = MockDevice::create();
+    const auto result = Device::create(kInvalidName, device);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+    // run test
+    const auto result = Device::create(kName, kInvalidDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getVersionStringError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, "");
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getTypeError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret =
+            makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_2::DeviceType::OTHER);
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedExtensionsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret =
+            makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, hidl_vec<V1_2::Extension>{});
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                        nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles + 1,
+                                        nn::kMaxNumberOfCacheFiles);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles,
+                                        nn::kMaxNumberOfCacheFiles + 1);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(
+            V1_0::ErrorStatus::GENERAL_FAILURE,
+            V1_2::Capabilities{
+                    .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+                    .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+            });
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, linkToDeathError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto& name = device->getName();
+
+    // verify result
+    EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify result
+    EXPECT_EQ(featureLevel, nn::Version::ANDROID_Q);
+}
+
+TEST(DeviceTest, getCachedData) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(1);
+
+    const auto result = Device::create(kName, mockDevice);
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& device = result.value();
+
+    // run test and verify results
+    EXPECT_EQ(device->getVersionString(), device->getVersionString());
+    EXPECT_EQ(device->getType(), device->getType());
+    EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+    EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+    EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, wait) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<void> { return {}; };
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& model, const auto& cb) {
+        cb(V1_0::ErrorStatus::NONE, std::vector<bool>(model.operations.size(), true));
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& supportedOperations = result.value();
+    EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+    EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& /*model*/, const auto& cb) {
+        cb(V1_0::ErrorStatus::GENERAL_FAILURE, {});
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_0::ErrorStatus::NONE,
+                                                     V1_0::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCache) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                              V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                              nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(V1_0::ErrorStatus::NONE,
+                                                              V1_0::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, allocateNotSupported) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/utils/test/MockBurstContext.h b/neuralnetworks/1.2/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e364178
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class MockBurstContext final : public IBurstContext {
+  public:
+    // V1_2 methods below.
+    MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.2/utils/test/MockDevice.h b/neuralnetworks/1.2/utils/test/MockDevice.h
new file mode 100644
index 0000000..0d34c70
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockDevice.h
@@ -0,0 +1,117 @@
+/*
+ * 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_1_2_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+using CacheToken =
+        hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+class MockDevice final : public IDevice {
+  public:
+    static sp<MockDevice> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations,
+                (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+                (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+    // V1_1 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+                (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+                (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+                 const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+
+    // V1_2 methods below.
+    MOCK_METHOD(Return<void>, getVersionString, (getVersionString_cb cb), (override));
+    MOCK_METHOD(Return<void>, getType, (getType_cb cb), (override));
+    MOCK_METHOD(Return<void>, getCapabilities_1_2, (getCapabilities_1_2_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedExtensions, (getSupportedExtensions_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_2,
+                (const V1_2::Model& model, getSupportedOperations_1_2_cb cb), (override));
+    MOCK_METHOD(Return<void>, getNumberOfCacheFilesNeeded, (getNumberOfCacheFilesNeeded_cb cb),
+                (override));
+    MOCK_METHOD(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));
+    MOCK_METHOD(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));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+    auto mockDevice = sp<MockDevice>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                            uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+    // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+    // to determine which object is dead. However, the utils::Device code only pairs a single death
+    // recipient with a single HIDL interface object, so these arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.2/utils/test/MockPreparedModel.h b/neuralnetworks/1.2/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..bd81712
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/MockPreparedModel.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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.2/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+  public:
+    static sp<MockPreparedModel> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+                             uint64_t /*cookie*/) override;
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+                (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+                (override));
+
+    // V1_2 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute_1_2,
+                (const V1_0::Request& request, V1_2::MeasureTiming measure,
+                 const sp<V1_2::IExecutionCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<void>, executeSynchronously,
+                (const V1_0::Request& request, V1_2::MeasureTiming measure,
+                 executeSynchronously_cb cb),
+                (override));
+    MOCK_METHOD(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));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+    auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                                   uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+    // in 0 and nullptr for these arguments instead. Normally, they are used by the
+    // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+    // code only pairs a single death recipient with a single HIDL interface object, so these
+    // arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_2_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..5e2ad79
--- /dev/null
+++ b/neuralnetworks/1.2/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,666 @@
+/*
+ * 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 "MockPreparedModel.h"
+
+#include "MockBurstContext.h"
+
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_2::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_2::IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = V1_2::Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+                                        .timeInDriver = std::numeric_limits<uint64_t>::max()};
+
+sp<MockPreparedModel> createMockPreparedModel() {
+    const auto mockPreparedModel = MockPreparedModel::create();
+
+    // Ensure that older calls are not used.
+    EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(0);
+
+    return mockPreparedModel;
+}
+
+auto makeExecuteSynchronously(V1_0::ErrorStatus status,
+                              const std::vector<V1_2::OutputShape>& outputShapes,
+                              const V1_2::Timing& timing) {
+    return [status, outputShapes, timing](const V1_0::Request& /*request*/,
+                                          V1_2::MeasureTiming /*measureTiming*/,
+                                          const V1_2::IPreparedModel::executeSynchronously_cb& cb) {
+        cb(status, outputShapes, timing);
+        return hardware::Void();
+    };
+}
+auto makeExecuteAsynchronously(V1_0::ErrorStatus launchStatus, V1_0::ErrorStatus returnStatus,
+                               const std::vector<V1_2::OutputShape>& outputShapes,
+                               const V1_2::Timing& timing) {
+    return [launchStatus, returnStatus, outputShapes, timing](
+                   const V1_0::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+                   const sp<V1_2::IExecutionCallback>& cb) -> Return<V1_0::ErrorStatus> {
+        cb->notify_1_2(returnStatus, outputShapes, timing);
+        return launchStatus;
+    };
+}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+                                       const sp<MockBurstContext>& burstContext) {
+    return [status, burstContext](
+                   const sp<V1_2::IBurstCallback>& /*callback*/,
+                   const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+                   const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+                   V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+        cb(status, burstContext);
+        return hardware::Void();
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+    // run test
+    const auto result = PreparedModel::create(kInvalidPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeSync) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeAsync) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::NONE,
+                                                       V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_0::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteSynchronously(V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_0::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_0::ErrorStatus::NONE, V1_0::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_0::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_0::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedNotSupported) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurst) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto mockBurstContext = sp<MockBurstContext>::make();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(
+                    makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // run test
+    const auto resource = preparedModel->getUnderlyingResource();
+
+    // verify resource
+    const sp<V1_2::IPreparedModel>* maybeMock = std::any_cast<sp<V1_2::IPreparedModel>>(&resource);
+    ASSERT_NE(maybeMock, nullptr);
+    EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_2::utils
diff --git a/neuralnetworks/1.2/vts/functional/Android.bp b/neuralnetworks/1.2/vts/functional/Android.bp
index 93edca6..e313b47 100644
--- a/neuralnetworks/1.2/vts/functional/Android.bp
+++ b/neuralnetworks/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalNeuralNetworksV1_2_utils",
     defaults: ["neuralnetworks_vts_functional_defaults"],
diff --git a/neuralnetworks/1.2/vts/functional/AndroidTest.xml b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
index 3f91618..5396d85 100644
--- a/neuralnetworks/1.2/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.2/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_2TargetTest" />
+        <option name="native-test-timeout" value="20m" />
     </test>
 </configuration>
diff --git a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
index 449b8f3..ede1600 100644
--- a/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.2/vts/functional/CompilationCachingTests.cpp
@@ -251,7 +251,7 @@
         for (uint32_t i = 0; i < mNumDataCache; i++) {
             mDataCache.push_back({mCacheDir + "data" + std::to_string(i)});
         }
-        // Dummy handles, use AccessMode::WRITE_ONLY for createCacheHandles to create files.
+        // Sample handles, use AccessMode::WRITE_ONLY for createCacheHandles to create files.
         hidl_vec<hidl_handle> modelHandle, dataHandle, tmpHandle;
         createCacheHandles(mModelCache, AccessMode::WRITE_ONLY, &modelHandle);
         createCacheHandles(mDataCache, AccessMode::WRITE_ONLY, &dataHandle);
@@ -469,18 +469,18 @@
         hidl_vec<hidl_handle> modelCache, dataCache;
         createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
         createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-        uint8_t dummyBytes[] = {0, 0};
-        // Write a dummy integer to the cache.
+        uint8_t sampleBytes[] = {0, 0};
+        // Write a sample integer to the cache.
         // The driver should be able to handle non-empty cache and non-zero fd offset.
         for (uint32_t i = 0; i < modelCache.size(); i++) {
-            ASSERT_EQ(write(modelCache[i].getNativeHandle()->data[0], &dummyBytes,
-                            sizeof(dummyBytes)),
-                      sizeof(dummyBytes));
+            ASSERT_EQ(write(modelCache[i].getNativeHandle()->data[0], &sampleBytes,
+                            sizeof(sampleBytes)),
+                      sizeof(sampleBytes));
         }
         for (uint32_t i = 0; i < dataCache.size(); i++) {
             ASSERT_EQ(
-                    write(dataCache[i].getNativeHandle()->data[0], &dummyBytes, sizeof(dummyBytes)),
-                    sizeof(dummyBytes));
+                    write(dataCache[i].getNativeHandle()->data[0], &sampleBytes, sizeof(sampleBytes)),
+                    sizeof(sampleBytes));
         }
         saveModelToCache(model, modelCache, dataCache);
     }
@@ -492,14 +492,14 @@
         hidl_vec<hidl_handle> modelCache, dataCache;
         createCacheHandles(mModelCache, AccessMode::READ_WRITE, &modelCache);
         createCacheHandles(mDataCache, AccessMode::READ_WRITE, &dataCache);
-        uint8_t dummyByte = 0;
+        uint8_t sampleByte = 0;
         // Advance the offset of each handle by one byte.
         // The driver should be able to handle non-zero fd offset.
         for (uint32_t i = 0; i < modelCache.size(); i++) {
-            ASSERT_GE(read(modelCache[i].getNativeHandle()->data[0], &dummyByte, 1), 0);
+            ASSERT_GE(read(modelCache[i].getNativeHandle()->data[0], &sampleByte, 1), 0);
         }
         for (uint32_t i = 0; i < dataCache.size(); i++) {
-            ASSERT_GE(read(dataCache[i].getNativeHandle()->data[0], &dummyByte, 1), 0);
+            ASSERT_GE(read(dataCache[i].getNativeHandle()->data[0], &sampleByte, 1), 0);
         }
         prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
         if (!mIsCachingSupported) {
@@ -1209,9 +1209,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type);
 }
 
-INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingTest,
-                        testing::Combine(kNamedDeviceChoices, kOperandTypeChoices),
-                        printCompilationCachingTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingTest,
+                         testing::Combine(kNamedDeviceChoices, kOperandTypeChoices),
+                         printCompilationCachingTest);
 
 using CompilationCachingSecurityTestParam = std::tuple<NamedDevice, OperandType, uint32_t>;
 
@@ -1365,9 +1366,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + std::to_string(seed));
 }
 
-INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
-                        testing::Combine(kNamedDeviceChoices, kOperandTypeChoices,
-                                         testing::Range(0U, 10U)),
-                        printCompilationCachingSecurityTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingSecurityTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingSecurityTest,
+                         testing::Combine(kNamedDeviceChoices, kOperandTypeChoices,
+                                          testing::Range(0U, 10U)),
+                         printCompilationCachingSecurityTest);
 
 }  // namespace android::hardware::neuralnetworks::V1_2::vts::functional
diff --git a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
index 98295ff..b72df88 100644
--- a/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.2/vts/functional/GeneratedTestHarness.h
@@ -47,6 +47,7 @@
 std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
 
 #define INSTANTIATE_GENERATED_TEST(TestSuite, filter)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                             \
     INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite,                                    \
                              testing::Combine(testing::ValuesIn(getNamedDevices()),       \
                                               testing::ValuesIn(getNamedModels(filter))), \
diff --git a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
index c4e2b15..a364b30 100644
--- a/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.2/vts/functional/VtsHalNeuralnetworks.h
@@ -42,6 +42,7 @@
         const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
 
 #define INSTANTIATE_DEVICE_TEST(TestSuite)                                                 \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                              \
     INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
                              printNeuralnetworksHidlTest)
 
diff --git a/neuralnetworks/1.3/Android.bp b/neuralnetworks/1.3/Android.bp
index 7b02cc5..aa8fc39 100644
--- a/neuralnetworks/1.3/Android.bp
+++ b/neuralnetworks/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.neuralnetworks@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IBuffer.hal",
diff --git a/neuralnetworks/1.3/IDevice.hal b/neuralnetworks/1.3/IDevice.hal
index e0b04a8..de889e4 100644
--- a/neuralnetworks/1.3/IDevice.hal
+++ b/neuralnetworks/1.3/IDevice.hal
@@ -131,6 +131,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Optionally, the driver may save the prepared model to cache during the
      * asynchronous preparation. Any error that occurs when saving to cache must
@@ -249,7 +257,15 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT}
      * or {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The
      * error due to an abort must be sent the same way as other errors,
-     * described above.
+     * described above. The deadline is represented as nanoseconds since the
+     * epoch of the steady clock (as if from
+     * std::chrono::steady_clock::time_point), but the service may convert it to
+     * the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * The only information that may be unknown to the model at this stage is
      * the shape of the tensors, which may only be known at execution time. As
diff --git a/neuralnetworks/1.3/IPreparedModel.hal b/neuralnetworks/1.3/IPreparedModel.hal
index e7d63f4..8b86a1a 100644
--- a/neuralnetworks/1.3/IPreparedModel.hal
+++ b/neuralnetworks/1.3/IPreparedModel.hal
@@ -74,6 +74,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
@@ -150,6 +158,14 @@
      * ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * Any number of calls to the execute* and executeSynchronously* functions,
      * in any combination, may be made concurrently, even on the same
@@ -231,6 +247,14 @@
      * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
      * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due
      * to an abort must be sent the same way as other errors, described above.
+     * The deadline is represented as nanoseconds since the epoch of the steady
+     * clock (as if from std::chrono::steady_clock::time_point), but the service
+     * may convert it to the nanoseconds since boot time (as if from
+     * clock_gettime(CLOCK_BOOTTIME, &ts) or
+     * android::base::boot_clock::time_point) to account for time when the
+     * system is suspended. This conversion can by done by finding the timeout
+     * duration remaining compared to the steady_clock and adding it to the
+     * current boot_clock time.
      *
      * If any of the sync fences in waitFor changes to error status after the executeFenced
      * call succeeds, or the execution is aborted because it cannot finish before the deadline
diff --git a/neuralnetworks/1.3/types.hal b/neuralnetworks/1.3/types.hal
index 3b2b14c..a26b858 100644
--- a/neuralnetworks/1.3/types.hal
+++ b/neuralnetworks/1.3/types.hal
@@ -263,7 +263,8 @@
      *      tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
      *      Since HAL version 1.2, for a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
      *      the scale and zeroPoint values can be different from
-     *      input tensors. Before HAL version 1.2 they have to be the same as for the input tensors.
+     *      input tensors. Before HAL version 1.2 they have to be the same as for the
+     *      input tensors.
      *      For a {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
      *      the scale and zeroPoint values can be different from input tensors.
      */
@@ -312,7 +313,8 @@
      * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
      * * * input.scale * filter.scale).
      *
-     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * Quantized signed with filter symmetric per channel quantization
+     *   (since HAL version 1.3):
      * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
      * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
      * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -425,8 +427,9 @@
      * Outputs:
      * * 0: The output 4-D tensor, of shape
      *      [batches, out_height, out_width, depth_out].
-     *      Before HAL version 1.2, for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
-     *      the following condition must be satisfied: output_scale > input_scale * filter_scale
+     *      Before HAL version 1.2, for output tensor of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, the following condition must
+     *      be satisfied: output_scale > input_scale * filter_scale
      */
     CONV_2D = @1.2::OperationType:CONV_2D,
 
@@ -477,7 +480,8 @@
      * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
      * * * input.scale * filter.scale).
      *
-     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * Quantized signed with filter symmetric per channel quantization
+     *   (since HAL version 1.3):
      * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
      * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
      * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -3354,7 +3358,8 @@
      * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
      * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
      *
-     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * Quantized signed with filter symmetric per channel quantization
+     *   (since HAL version 1.3):
      * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
      * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
      * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -3829,7 +3834,7 @@
      *      front of dimension i.
      *      padding[i, 1] specifies the number of elements to be padded after
      *      the end of dimension i.
-     * * 2: An scalar specifying the value to use for padding input0.
+     * * 2: A scalar specifying the value to use for padding input0.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
      *      pad value must be of {@link OperandType::FLOAT16}.
      *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
@@ -4615,7 +4620,8 @@
      * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
      * * * input.scale * filter.scale).
      *
-     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * Quantized signed with filter symmetric per channel quantization
+     *   (since HAL version 1.3):
      * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
      * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
      * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
@@ -5103,8 +5109,8 @@
      * signature of this operation. That is, if the operation has (3 + n) inputs
      * and m outputs, both subgraphs must have n inputs and m outputs with the same
      * types, ranks, dimensions, scales,
-     * zeroPoints, and extraParams as the corresponding operation inputs and
-     * outputs.
+     * zeroPoints, and extraParams as the corresponding operation
+     * inputs and outputs.
      * All of the operands mentioned must have fully specified dimensions.
      *
      * Inputs:
@@ -5170,15 +5176,15 @@
      * * 0: A {@link OperandType::SUBGRAPH} reference to the condition
      *      subgraph. The subgraph must have (m + k + n) inputs with
      *      the same types, ranks, dimensions,
-     *      scales, zeroPoints, and extraParams as the corresponding inputs of
-     *      the WHILE operation and exactly one output of
-     *      {@link OperandType::TENSOR_BOOL8} and shape [1].
+     *      scales, zeroPoints, and extraParams as the
+     *      corresponding inputs of the WHILE operation and exactly one output
+     *      of {@link OperandType::TENSOR_BOOL8} and shape [1].
      *      All of the operands mentioned must have fully specified dimensions.
      * * 1: A {@link OperandType::SUBGRAPH} reference to the body subgraph.
      *      The subgraph must have (m + k + n) inputs and (m + k) outputs with
      *      the same types, ranks, dimensions,
-     *      scales, zeroPoints, and extraParams as the corresponding inputs and
-     *      outputs of the WHILE operation.
+     *      scales, zeroPoints, and extraParams as the
+     *      corresponding inputs and outputs of the WHILE operation.
      *      All of the operands mentioned must have fully specified dimensions.
      * * (m inputs): Initial values for input-output operands.
      * * (k inputs): Initial values for state-only operands.
@@ -5334,7 +5340,6 @@
     HIGH,
 };
 
-
 /**
  * The capabilities of a driver.
  *
@@ -5538,15 +5543,18 @@
     /**
      * Quantized scale of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
-     * TENSOR_INT32.
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
      */
     float scale;
 
     /**
      * Quantized zero-point offset of the operand.
      *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
      */
     int32_t zeroPoint;
 
@@ -5740,8 +5748,8 @@
      * Input data and information to be used in the execution of a prepared
      * model.
      *
-     * The index of the input corresponds to the index in Model.inputIndexes.
-     *   E.g., input[i] corresponds to Model.inputIndexes[i].
+     * The index of the input corresponds to the index in Model.main.inputIndexes.
+     *   E.g., input[i] corresponds to Model.main.inputIndexes[i].
      */
     vec<RequestArgument> inputs;
 
@@ -5749,8 +5757,8 @@
      * Output data and information to be used in the execution of a prepared
      * model.
      *
-     * The index of the output corresponds to the index in Model.outputIndexes.
-     *   E.g., output[i] corresponds to Model.outputIndexes[i].
+     * The index of the output corresponds to the index in Model.main.outputIndexes.
+     *   E.g., output[i] corresponds to Model.main.outputIndexes[i].
      */
     vec<RequestArgument> outputs;
 
diff --git a/neuralnetworks/1.3/types.t b/neuralnetworks/1.3/types.t
index 7220e37..96d1a1b 100644
--- a/neuralnetworks/1.3/types.t
+++ b/neuralnetworks/1.3/types.t
@@ -90,457 +90,25 @@
     BASE_MAX        = 0xFFFF,
 };
 
-/**
- * Priority given to a prepared model for execution.
- */
-enum Priority : int32_t {
-    LOW,
-    MEDIUM,
-    HIGH,
-};
+%insert Priority
 
+%insert Capabilities
 
-/**
- * The capabilities of a driver.
- *
- * This represents performance of non-extension operations.
- *
- * Performance of an operation other than {@link OperationType::IF} and
- * {@link OperationType::WHILE} comes from the type of its first operand.
- */
-struct Capabilities {
-    /**
-     * Driver performance when operating on float32 data but performing
-     * calculations with range and/or precision as low as that of the IEEE
-     * 754 16-bit floating-point format.
-     */
-    PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
-    PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+%insert Operation
 
-    /**
-     * Driver performance when operating on a particular data type.
-     * In the case of float32 data, this is used when the calculations
-     * are not relaxed.
-     */
-    struct OperandPerformance {
-        OperandType type;
-        PerformanceInfo info;
-    };
+%insert OperandLifeTime
 
-    /**
-     * Performance by operand type. Must be sorted by OperandType.
-     *
-     * If a particular {@link OperandType} is not present in operandPerformance,
-     * its performance is treated as
-     * { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
-     *
-     * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver
-     * must not report operand performance for {@link OperandType::SUBGRAPH}.
-     */
-    vec<OperandPerformance> operandPerformance;
+%insert Operand
 
-    /**
-     * Performance of an {@link OperationType::IF} operation is the sum of
-     * {@link Capabilities::ifPerformance} and the mean of performance for the
-     * two branch subgraphs, where performance for a subgraph is the sum of the
-     * performance of all operations within the subgraph.
-     */
-    PerformanceInfo ifPerformance;
+%insert Model
 
-    /**
-     * Performance of a {@link OperationType::WHILE} operation is the sum of
-     * {@link Capabilities::whilePerformance}, performance for the condition
-     * subgraph and performance for the body subgraph, where performance for a
-     * subgraph is the sum of the performance of all operations within the
-     * subgraph.
-     */
-    PerformanceInfo whilePerformance;
-};
+%insert Subgraph
 
-/**
- * Describes one operation of the model's graph.
- */
-struct Operation {
-    /**
-     * The operation type.
-     *
-     * Besides the values listed in {@link OperationType}, any value above
-     * {@link OperationTypeRange::BASE_MAX} is possible and should be interpreted
-     * as an extension type according to {@link Model::extensionNameToPrefix}.
-     */
-    OperationType type;
+%insert BufferDesc
 
-    /**
-     * Describes the table that contains the indexes of the inputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> inputs;
+%insert BufferRole
 
-    /**
-     * Describes the table that contains the indexes of the outputs of the
-     * operation. The offset is the index in the operandIndexes table.
-     */
-    vec<uint32_t> outputs;
-};
-
-/**
- * How an operand is used.
- */
-enum OperandLifeTime : int32_t {
-    /**
-     * The operand is internal to the model. It's created by an operation and
-     * consumed by other operations. It must be an output operand of
-     * exactly one operation.
-     */
-    TEMPORARY_VARIABLE,
-
-    /**
-     * The operand is an input of a subgraph. It must not be an output
-     * operand of any operation.
-     *
-     * An operand can't be both input and output of a subgraph.
-     */
-    SUBGRAPH_INPUT,
-
-    /**
-     * The operand is an output of a subgraph. It must be an output
-     * operand of exactly one operation.
-     *
-     * An operand can't be both input and output of a subgraph.
-     */
-    SUBGRAPH_OUTPUT,
-
-    /**
-     * The operand is a constant found in Model.operandValues. It must
-     * not be an output operand of any operation.
-     */
-    CONSTANT_COPY,
-
-    /**
-     * The operand is a constant that was specified via a Memory
-     * object. It must not be an output operand of any operation.
-     */
-    CONSTANT_REFERENCE,
-
-    /**
-     * The operand does not have a value. This is valid only for optional
-     * arguments of operations.
-     */
-    NO_VALUE,
-
-    /**
-     * The operand is a reference to a subgraph. It must be an input to one
-     * or more {@link OperationType::IF} or {@link OperationType::WHILE}
-     * operations.
-     */
-    SUBGRAPH,
-};
-
-/**
- * Describes one operand of the model's graph.
- */
-struct Operand {
-    /**
-     * The data type.
-     *
-     * Besides the values listed in {@link OperandType}, any value above
-     * {@link OperandTypeRange::BASE_MAX} is possible and should be interpreted
-     * as an extension type according to {@link Model::extensionNameToPrefix}.
-     */
-    OperandType type;
-
-    /**
-     * Dimensions of the operand.
-     *
-     * For a scalar operand, dimensions.size() must be 0.
-     *
-     * A tensor operand with all dimensions specified has "fully
-     * specified" dimensions. Whenever possible (i.e., whenever the
-     * dimensions are known at model construction time), a tensor
-     * operand should have (but is not required to have) fully
-     * specified dimensions, in order to enable the best possible
-     * performance.
-     *
-     * If a tensor operand's dimensions are not fully specified, the
-     * dimensions of the operand are deduced from the operand
-     * dimensions and values of the operation for which that operand
-     * is an output or from the corresponding {@link OperationType::IF} or
-     * {@link OperationType::WHILE} operation input operand dimensions in the
-     * case of referenced subgraph input operands.
-     *
-     * In the following situations, a tensor operand's dimensions must
-     * be fully specified:
-     *
-     *     . The operand has lifetime CONSTANT_COPY or
-     *       CONSTANT_REFERENCE.
-     *
-     *     . The operand has lifetime SUBGRAPH_INPUT and belongs to the main
-     *       subgraph. Fully specified dimensions must either be present in the
-     *       Operand or they must be provided in the corresponding
-     *       RequestArgument.
-     *       EXCEPTION: If the input is optional and omitted
-     *       (by setting the hasNoValue field of the corresponding
-     *       RequestArgument to true) then it need not have fully
-     *       specified dimensions.
-     *
-     * A tensor operand with some number of unspecified dimensions is
-     * represented by setting each unspecified dimension to 0.
-     *
-     * A tensor operand with unspecified rank is represented by providing
-     * an empty dimensions vector.
-     */
-    vec<uint32_t> dimensions;
-
-    /**
-     * The number of times this operand appears as an operation input.
-     *
-     * (For example, if this operand appears once in one operation's
-     * input list, and three times in another operation's input list,
-     * then numberOfConsumers = 4.)
-     */
-    uint32_t numberOfConsumers;
-
-    /**
-     * Quantized scale of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM or
-     * TENSOR_INT32.
-     */
-    float scale;
-
-    /**
-     * Quantized zero-point offset of the operand.
-     *
-     * Only applicable if the operand is of type TENSOR_QUANT8_ASYMM.
-     */
-    int32_t zeroPoint;
-
-    /**
-     * How the operand is used.
-     */
-    OperandLifeTime lifetime;
-
-    /**
-     * Where to find the data for this operand.
-     * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT,
-     * or NO_VALUE:
-     * - All the fields must be 0.
-     * If the lifetime is CONSTANT_COPY:
-     * - location.poolIndex is 0.
-     * - location.offset is the offset in bytes into Model.operandValues.
-     * - location.length is set.
-     * If the lifetime is CONSTANT_REFERENCE:
-     * - location.poolIndex is set.
-     * - location.offset is the offset in bytes into the specified pool.
-     * - location.length is set.
-     * If the lifetime is SUBGRAPH:
-     * - location.poolIndex is 0.
-     * - location.offset is the index of the referenced subgraph in
-     *   {@link Model::referenced}.
-     * - location.length is 0.
-     */
-    DataLocation location;
-
-    /**
-     * Additional parameters specific to a particular operand type.
-     */
-    @1.2::Operand.ExtraParams extraParams;
-};
-
-/**
- * A Neural Network Model.
- *
- * This includes not only the execution graph, but also constant data such as
- * weights or scalars added at construction time. The only information that
- * may not be known is the shape of the input tensors.
- */
-struct Model {
-    /**
-     * The top-level subgraph.
-     */
-    Subgraph main;
-
-    /**
-     * Referenced subgraphs.
-     *
-     * Each subgraph is referenced by the main subgraph or at least one other
-     * referenced subgraph.
-     *
-     * There must be no reference cycles.
-     */
-    vec<Subgraph> referenced;
-
-    /**
-     * A byte buffer containing operand data that were copied into the model.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_COPY.
-     */
-    vec<uint8_t> operandValues;
-
-    /**
-     * A collection of shared memory pools containing operand values.
-     *
-     * An operand's value must be located here if and only if Operand::lifetime
-     * equals OperandLifeTime::CONSTANT_REFERENCE.
-     */
-    vec<memory> pools;
-
-    /**
-     * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or
-     * precision as low as that of the IEEE 754 16-bit floating-point format.
-     * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the
-     * range and precision of the IEEE 754 32-bit floating-point format.
-     */
-    bool relaxComputationFloat32toFloat16;
-
-    /**
-     * The mapping between extension names and prefixes of operand and
-     * operation type values.
-     *
-     * An operand or operation whose numeric type value is above
-     * {@link OperandTypeRange::BASE_MAX} or
-     * {@link OperationTypeRange::BASE_MAX} respectively should be interpreted
-     * as an extension operand. The low
-     * {@link @1.2::Model::ExtensionTypeEncoding::LOW_BITS_TYPE} bits of the
-     * value correspond to the type ID within the extension and the high
-     * {@link @1.2::Model::ExtensionTypeEncoding::HIGH_BITS_PREFIX} bits encode
-     * the "prefix", which maps uniquely to the extension name.
-     *
-     * For example, if a model contains an operation whose value is
-     * 0xAAAABBBB and extensionNameToPrefix contains an entry with
-     * prefix=0xAAAA and name="vendor.test.test_extension", then
-     * the operation should be interpreted as the operation 0xBBBB
-     * of the extension named vendor.test.test_extension.
-     *
-     * This is a one-to-one correspondence. That is, there must be at most one
-     * prefix corresponding to each extension name and at most one extension
-     * name corresponding to each prefix.
-     */
-    vec<@1.2::Model.ExtensionNameAndPrefix> extensionNameToPrefix;
-};
-
-/**
- * An excerpt of the execution graph.
- */
-struct Subgraph {
-    /**
-     * All operands included in the subgraph.
-     */
-    vec<Operand> operands;
-
-    /**
-     * All operations included in the subgraph.
-     *
-     * The operations are sorted into execution order. Every operand
-     * with lifetime SUBGRAPH_OUTPUT or TEMPORARY_VARIABLE must be
-     * written before it is read.
-     */
-    vec<Operation> operations;
-
-    /**
-     * Input indexes of the subgraph. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> inputIndexes;
-
-    /**
-     * Output indexes of the subgraph. There must be at least one.
-     *
-     * Each value corresponds to the index of the operand in "operands".
-     */
-    vec<uint32_t> outputIndexes;
-};
-
-/**
- * A buffer descriptor. Describes the properties of a buffer.
- */
-struct BufferDesc {
-    /**
-     * Dimensions of the buffer. May have unknown dimensions or rank. A buffer with some number
-     * of unspecified dimensions is represented by setting each unspecified dimension to 0. A
-     * buffer with unspecified rank is represented by providing an empty dimensions vector.
-     */
-    vec<uint32_t> dimensions;
-};
-
-/**
- * Describes a role of an input or output to a prepared model.
- */
-struct BufferRole {
-    /**
-     * The index of the IPreparedModel within the "preparedModel" argument passed in
-     * IDevice::allocate.
-     */
-    uint32_t modelIndex;
-
-    /**
-     * The index of the input or output operand.
-     */
-    uint32_t ioIndex;
-
-    /**
-     * A floating-point value within the range (0.0, 1.0]. Describes how likely the
-     * buffer is to be used in the specified role. This is provided as a hint to
-     * optimize the case when multiple roles prefer different buffer locations or data
-     * layouts.
-     */
-    float frequency;
-};
-
-/**
- * Inputs to be sent to and outputs to be retrieved from a prepared model.
- *
- * A Request serves two primary tasks:
- * 1) Provides the input and output data to be used when executing the model.
- * 2) Specifies any updates to the input operand metadata that were left
- *    unspecified at model preparation time.
- *
- * An output must not overlap with any other output, with an input, or
- * with an operand of lifetime CONSTANT_REFERENCE.
- */
-struct Request {
-    /**
-     * Input data and information to be used in the execution of a prepared
-     * model.
-     *
-     * The index of the input corresponds to the index in Model.inputIndexes.
-     *   E.g., input[i] corresponds to Model.inputIndexes[i].
-     */
-    vec<RequestArgument> inputs;
-
-    /**
-     * Output data and information to be used in the execution of a prepared
-     * model.
-     *
-     * The index of the output corresponds to the index in Model.outputIndexes.
-     *   E.g., output[i] corresponds to Model.outputIndexes[i].
-     */
-    vec<RequestArgument> outputs;
-
-    /**
-     * A memory pool.
-     */
-    safe_union MemoryPool {
-        /**
-         * Specifies a client-managed shared memory pool.
-         */
-        memory hidlMemory;
-
-        /**
-         * Specifies a driver-managed buffer. It is the token returned from IDevice::allocate,
-         * and is specific to the IDevice object.
-         */
-        uint32_t token;
-    };
-
-    /**
-     * A collection of memory pools containing operand data for both the
-     * inputs and the outputs to a model.
-     */
-    vec<MemoryPool> pools;
-};
+%insert Request
 
 /**
  * Optional time point of the steady clock (as from std::chrono::steady_clock)
diff --git a/neuralnetworks/1.3/utils/Android.bp b/neuralnetworks/1.3/utils/Android.bp
new file mode 100644
index 0000000..28c036a
--- /dev/null
+++ b/neuralnetworks/1.3/utils/Android.bp
@@ -0,0 +1,82 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_1_3",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal/1.3/"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
+        "libfmq",
+    ],
+    export_static_lib_headers: [
+        "neuralnetworks_utils_hal_common",
+    ],
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_1_3_test",
+    srcs: ["test/*.cpp"],
+    static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+        "neuralnetworks_utils_hal_1_0",
+        "neuralnetworks_utils_hal_1_1",
+        "neuralnetworks_utils_hal_1_2",
+        "neuralnetworks_utils_hal_1_3",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/1.3/utils/OWNERS b/neuralnetworks/1.3/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/1.3/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
new file mode 100644
index 0000000..69e87f7
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Buffer.h
@@ -0,0 +1,56 @@
+/*
+ * 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_1_3_UTILS_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_BUFFER_H
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+// Class that adapts V1_3::IBuffer to nn::IBuffer.
+class Buffer final : public nn::IBuffer {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Buffer>> create(
+            sp<V1_3::IBuffer> buffer, nn::Request::MemoryDomainToken token);
+
+    Buffer(PrivateConstructorTag tag, sp<V1_3::IBuffer> buffer,
+           nn::Request::MemoryDomainToken token);
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
+                                     const nn::Dimensions& dimensions) const override;
+
+  private:
+    const sp<V1_3::IBuffer> kBuffer;
+    const nn::Request::MemoryDomainToken kToken;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_BUFFER_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
new file mode 100644
index 0000000..643172e
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Callbacks.h
@@ -0,0 +1,103 @@
+/*
+ * 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_1_3_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_CALLBACKS_H
+
+#include <android/hardware/neuralnetworks/1.0/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.0/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.2/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+// 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::V1_3::utils {
+
+// Converts the results of IDevice::getSupportedOperations* to the NN canonical format. On success,
+// this function returns with the supported operations as indicated by a driver. On failure, this
+// function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+        ErrorStatus status, const hidl_vec<bool>& supportedOperations);
+
+// Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
+// function returns with a non-null nn::SharedPreparedModel with a feature level of
+// nn::Version::ANDROID_R. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        ErrorStatus status, const sp<IPreparedModel>& preparedModel);
+
+// Converts the results of IDevice::execute* to the NN canonical format. On success, this function
+// returns with the output shapes and the timing information. On failure, this function returns with
+// the appropriate nn::ExecutionError.
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+        const V1_2::Timing& timing);
+
+// A HIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
+class PreparedModelCallback final : public IPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    Return<void> notify(V1_0::ErrorStatus status,
+                        const sp<V1_0::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const sp<V1_2::IPreparedModel>& preparedModel) override;
+    Return<void> notify_1_3(ErrorStatus status, const sp<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+// A HIDL callback class to receive the results of IDevice::execute_1_3 asynchronously.
+class ExecutionCallback final : public IExecutionCallback, public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>;
+
+    Return<void> notify(V1_0::ErrorStatus status) override;
+    Return<void> notify_1_2(V1_0::ErrorStatus status,
+                            const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+    Return<void> notify_1_3(ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+                            const V1_2::Timing& timing) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
new file mode 100644
index 0000000..b677c62
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Conversions.h
@@ -0,0 +1,118 @@
+/*
+ * 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_1_3_CONVERSIONS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_CONVERSIONS_H
+
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+namespace android::nn {
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType);
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority);
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_3::Capabilities::OperandPerformance& operandPerformance);
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime);
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand);
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model);
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph);
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc);
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole);
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request);
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool);
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint);
+GeneralResult<OptionalDuration> unvalidatedConvert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& errorStatus);
+
+GeneralResult<Priority> convert(const hal::V1_3::Priority& priority);
+GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities);
+GeneralResult<Model> convert(const hal::V1_3::Model& model);
+GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc);
+GeneralResult<Request> convert(const hal::V1_3::Request& request);
+GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint);
+GeneralResult<OptionalDuration> convert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration);
+GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus);
+
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle);
+GeneralResult<std::vector<BufferRole>> convert(
+        const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles);
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority);
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& operandLifeTime);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph);
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+        const nn::OptionalDuration& optionalTimeoutDuration);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
+
+nn::GeneralResult<Priority> convert(const nn::Priority& priority);
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<OptionalTimeoutDuration> convert(
+        const nn::OptionalDuration& optionalTimeoutDuration);
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
+
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle);
+nn::GeneralResult<hidl_memory> convert(const nn::SharedMemory& memory);
+nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus);
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+        const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<hidl_vec<V1_2::Extension>> convert(const std::vector<nn::Extension>& extensions);
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<hidl_vec<V1_2::OutputShape>> convert(
+        const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<V1_2::DeviceType> convert(const nn::DeviceType& deviceType);
+nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing);
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_CONVERSIONS_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
new file mode 100644
index 0000000..84f606a
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Device.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_3::utils {
+
+// Class that adapts V1_3::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(std::string name,
+                                                                   sp<V1_3::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+           nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+           nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+           sp<V1_3::IDevice> device, hal::utils::DeathHandler deathHandler);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::DeviceType kDeviceType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+    const sp<V1_3::IDevice> kDevice;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_DEVICE_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
new file mode 100644
index 0000000..06c33d4
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Execution.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_3::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+            OptionalTimeoutDuration loopTimeoutDuration);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation,
+              V1_2::MeasureTiming measure, OptionalTimeoutDuration loopTimeoutDuration);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const V1_2::MeasureTiming kMeasure;
+    const OptionalTimeoutDuration kLoopTimeoutDuration;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_EXECUTION_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
new file mode 100644
index 0000000..5acba71
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/PreparedModel.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_3::utils {
+
+// Class that adapts V1_3::IPreparedModel to nn::IPreparedModel.
+class PreparedModel final : public nn::IPreparedModel,
+                            public std::enable_shared_from_this<PreparedModel> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            sp<V1_3::IPreparedModel> preparedModel, bool executeSynchronously);
+
+    PreparedModel(PrivateConstructorTag tag, bool executeSynchronously,
+                  sp<V1_3::IPreparedModel> preparedModel, hal::utils::DeathHandler deathHandler);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+    executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+                          V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+                          const OptionalTimeoutDuration& loopTimeoutDuration,
+                          const OptionalTimeoutDuration& timeoutDurationAfterFence,
+                          const hal::utils::RequestRelocation& relocation) const;
+
+  private:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeSynchronously(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration) const;
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeAsynchronously(
+            const Request& request, V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+            const OptionalTimeoutDuration& loopTimeoutDuration) const;
+
+    const bool kExecuteSynchronously;
+    const sp<V1_3::IPreparedModel> kPreparedModel;
+    const hal::utils::DeathHandler kDeathHandler;
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h
new file mode 100644
index 0000000..2bc3257
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Service.h
@@ -0,0 +1,31 @@
+/*
+ * 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_1_3_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_SERVICE_H
diff --git a/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
new file mode 100644
index 0000000..1d76caa
--- /dev/null
+++ b/neuralnetworks/1.3/utils/include/nnapi/hal/1.3/Utils.h
@@ -0,0 +1,80 @@
+/*
+ * 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_1_3_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_H
+
+#include "nnapi/hal/1.3/Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.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/HandleError.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+using V1_1::utils::kDefaultExecutionPreference;
+using V1_2::utils::CacheToken;
+using V1_2::utils::kDefaultMesaureTiming;
+using V1_2::utils::kNoTiming;
+
+constexpr auto kDefaultPriority = Priority::MEDIUM;
+constexpr auto kVersion = nn::Version::ANDROID_R;
+
+template <typename Type>
+nn::Result<void> validate(const Type& halObject) {
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
+    }
+    return {};
+}
+
+template <typename Type>
+bool valid(const Type& halObject) {
+    const auto result = utils::validate(halObject);
+    if (!result.has_value()) {
+        LOG(ERROR) << result.error();
+    }
+    return result.has_value();
+}
+
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(hal::utils::makeGeneralFailure(nn::validate(canonical)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+        -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+    return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_H
diff --git a/neuralnetworks/1.3/utils/src/Assertions.cpp b/neuralnetworks/1.3/utils/src/Assertions.cpp
new file mode 100644
index 0000000..96d647a
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Assertions.cpp
@@ -0,0 +1,218 @@
+/*
+ * 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/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Types.h>
+#include <type_traits>
+
+namespace {
+
+#define COMPARE_ENUMS_TYPES(lhsType, rhsType)                                                   \
+    static_assert(                                                                              \
+            std::is_same_v<                                                                     \
+                    std::underlying_type_t<::android::hardware::neuralnetworks::V1_3::lhsType>, \
+                    std::underlying_type_t<::android::nn::rhsType>>,                            \
+            "::android::hardware::neuralnetworks::V1_3::" #lhsType                              \
+            " does not have the same underlying type as ::android::nn::" #rhsType)
+
+COMPARE_ENUMS_TYPES(OperandType, OperandType);
+COMPARE_ENUMS_TYPES(OperationType, OperationType);
+COMPARE_ENUMS_TYPES(Priority, Priority);
+COMPARE_ENUMS_TYPES(OperandLifeTime, Operand::LifeTime);
+COMPARE_ENUMS_TYPES(ErrorStatus, ErrorStatus);
+
+#undef COMPARE_ENUMS_TYPES
+
+#define COMPARE_ENUMS_FULL(symbol, lhsType, rhsType)                                             \
+    static_assert(                                                                               \
+            static_cast<                                                                         \
+                    std::underlying_type_t<::android::hardware::neuralnetworks::V1_3::lhsType>>( \
+                    ::android::hardware::neuralnetworks::V1_3::lhsType::symbol) ==               \
+                    static_cast<std::underlying_type_t<::android::nn::rhsType>>(                 \
+                            ::android::nn::rhsType::symbol),                                     \
+            "::android::hardware::neuralnetworks::V1_3::" #lhsType "::" #symbol                  \
+            " does not match ::android::nn::" #rhsType "::" #symbol)
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperandType, OperandType)
+
+COMPARE_ENUMS(FLOAT32);
+COMPARE_ENUMS(INT32);
+COMPARE_ENUMS(UINT32);
+COMPARE_ENUMS(TENSOR_FLOAT32);
+COMPARE_ENUMS(TENSOR_INT32);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM);
+COMPARE_ENUMS(BOOL);
+COMPARE_ENUMS(TENSOR_QUANT16_SYMM);
+COMPARE_ENUMS(TENSOR_FLOAT16);
+COMPARE_ENUMS(TENSOR_BOOL8);
+COMPARE_ENUMS(FLOAT16);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM_PER_CHANNEL);
+COMPARE_ENUMS(TENSOR_QUANT16_ASYMM);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM_SIGNED);
+COMPARE_ENUMS(SUBGRAPH);
+COMPARE_ENUMS(OEM);
+COMPARE_ENUMS(TENSOR_OEM_BYTE);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperationType, OperationType)
+
+COMPARE_ENUMS(ADD);
+COMPARE_ENUMS(AVERAGE_POOL_2D);
+COMPARE_ENUMS(CONCATENATION);
+COMPARE_ENUMS(CONV_2D);
+COMPARE_ENUMS(DEPTHWISE_CONV_2D);
+COMPARE_ENUMS(DEPTH_TO_SPACE);
+COMPARE_ENUMS(DEQUANTIZE);
+COMPARE_ENUMS(EMBEDDING_LOOKUP);
+COMPARE_ENUMS(FLOOR);
+COMPARE_ENUMS(FULLY_CONNECTED);
+COMPARE_ENUMS(HASHTABLE_LOOKUP);
+COMPARE_ENUMS(L2_NORMALIZATION);
+COMPARE_ENUMS(L2_POOL_2D);
+COMPARE_ENUMS(LOCAL_RESPONSE_NORMALIZATION);
+COMPARE_ENUMS(LOGISTIC);
+COMPARE_ENUMS(LSH_PROJECTION);
+COMPARE_ENUMS(LSTM);
+COMPARE_ENUMS(MAX_POOL_2D);
+COMPARE_ENUMS(MUL);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+COMPARE_ENUMS(RESHAPE);
+COMPARE_ENUMS(RESIZE_BILINEAR);
+COMPARE_ENUMS(RNN);
+COMPARE_ENUMS(SOFTMAX);
+COMPARE_ENUMS(SPACE_TO_DEPTH);
+COMPARE_ENUMS(SVDF);
+COMPARE_ENUMS(TANH);
+COMPARE_ENUMS(BATCH_TO_SPACE_ND);
+COMPARE_ENUMS(DIV);
+COMPARE_ENUMS(MEAN);
+COMPARE_ENUMS(PAD);
+COMPARE_ENUMS(SPACE_TO_BATCH_ND);
+COMPARE_ENUMS(SQUEEZE);
+COMPARE_ENUMS(STRIDED_SLICE);
+COMPARE_ENUMS(SUB);
+COMPARE_ENUMS(TRANSPOSE);
+COMPARE_ENUMS(ABS);
+COMPARE_ENUMS(ARGMAX);
+COMPARE_ENUMS(ARGMIN);
+COMPARE_ENUMS(AXIS_ALIGNED_BBOX_TRANSFORM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(BOX_WITH_NMS_LIMIT);
+COMPARE_ENUMS(CAST);
+COMPARE_ENUMS(CHANNEL_SHUFFLE);
+COMPARE_ENUMS(DETECTION_POSTPROCESSING);
+COMPARE_ENUMS(EQUAL);
+COMPARE_ENUMS(EXP);
+COMPARE_ENUMS(EXPAND_DIMS);
+COMPARE_ENUMS(GATHER);
+COMPARE_ENUMS(GENERATE_PROPOSALS);
+COMPARE_ENUMS(GREATER);
+COMPARE_ENUMS(GREATER_EQUAL);
+COMPARE_ENUMS(GROUPED_CONV_2D);
+COMPARE_ENUMS(HEATMAP_MAX_KEYPOINT);
+COMPARE_ENUMS(INSTANCE_NORMALIZATION);
+COMPARE_ENUMS(LESS);
+COMPARE_ENUMS(LESS_EQUAL);
+COMPARE_ENUMS(LOG);
+COMPARE_ENUMS(LOGICAL_AND);
+COMPARE_ENUMS(LOGICAL_NOT);
+COMPARE_ENUMS(LOGICAL_OR);
+COMPARE_ENUMS(LOG_SOFTMAX);
+COMPARE_ENUMS(MAXIMUM);
+COMPARE_ENUMS(MINIMUM);
+COMPARE_ENUMS(NEG);
+COMPARE_ENUMS(NOT_EQUAL);
+COMPARE_ENUMS(PAD_V2);
+COMPARE_ENUMS(POW);
+COMPARE_ENUMS(PRELU);
+COMPARE_ENUMS(QUANTIZE);
+COMPARE_ENUMS(QUANTIZED_16BIT_LSTM);
+COMPARE_ENUMS(RANDOM_MULTINOMIAL);
+COMPARE_ENUMS(REDUCE_ALL);
+COMPARE_ENUMS(REDUCE_ANY);
+COMPARE_ENUMS(REDUCE_MAX);
+COMPARE_ENUMS(REDUCE_MIN);
+COMPARE_ENUMS(REDUCE_PROD);
+COMPARE_ENUMS(REDUCE_SUM);
+COMPARE_ENUMS(ROI_ALIGN);
+COMPARE_ENUMS(ROI_POOLING);
+COMPARE_ENUMS(RSQRT);
+COMPARE_ENUMS(SELECT);
+COMPARE_ENUMS(SIN);
+COMPARE_ENUMS(SLICE);
+COMPARE_ENUMS(SPLIT);
+COMPARE_ENUMS(SQRT);
+COMPARE_ENUMS(TILE);
+COMPARE_ENUMS(TOPK_V2);
+COMPARE_ENUMS(TRANSPOSE_CONV_2D);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(RESIZE_NEAREST_NEIGHBOR);
+COMPARE_ENUMS(QUANTIZED_LSTM);
+COMPARE_ENUMS(IF);
+COMPARE_ENUMS(WHILE);
+COMPARE_ENUMS(ELU);
+COMPARE_ENUMS(HARD_SWISH);
+COMPARE_ENUMS(FILL);
+COMPARE_ENUMS(RANK);
+COMPARE_ENUMS(OEM_OPERATION);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, Priority, Priority)
+
+COMPARE_ENUMS(LOW);
+COMPARE_ENUMS(MEDIUM);
+COMPARE_ENUMS(HIGH);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, OperandLifeTime, Operand::LifeTime)
+
+COMPARE_ENUMS(TEMPORARY_VARIABLE);
+COMPARE_ENUMS(SUBGRAPH_INPUT);
+COMPARE_ENUMS(SUBGRAPH_OUTPUT);
+COMPARE_ENUMS(CONSTANT_COPY);
+COMPARE_ENUMS(CONSTANT_REFERENCE);
+COMPARE_ENUMS(NO_VALUE);
+COMPARE_ENUMS(SUBGRAPH);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, ErrorStatus, ErrorStatus)
+
+COMPARE_ENUMS(NONE);
+COMPARE_ENUMS(DEVICE_UNAVAILABLE);
+COMPARE_ENUMS(GENERAL_FAILURE);
+COMPARE_ENUMS(OUTPUT_INSUFFICIENT_SIZE);
+COMPARE_ENUMS(INVALID_ARGUMENT);
+COMPARE_ENUMS(MISSED_DEADLINE_TRANSIENT);
+COMPARE_ENUMS(MISSED_DEADLINE_PERSISTENT);
+COMPARE_ENUMS(RESOURCE_EXHAUSTED_TRANSIENT);
+COMPARE_ENUMS(RESOURCE_EXHAUSTED_PERSISTENT);
+
+#undef COMPARE_ENUMS
+
+#undef COMPARE_ENUMS_FULL
+
+}  // anonymous namespace
diff --git a/neuralnetworks/1.3/utils/src/Buffer.cpp b/neuralnetworks/1.3/utils/src/Buffer.cpp
new file mode 100644
index 0000000..ada5265
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Buffer.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/HandleError.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <memory>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes.
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<std::shared_ptr<const Buffer>> Buffer::create(
+        sp<V1_3::IBuffer> buffer, nn::Request::MemoryDomainToken token) {
+    if (buffer == nullptr) {
+        return NN_ERROR() << "V1_3::utils::Buffer::create must have non-null buffer";
+    }
+    if (token == static_cast<nn::Request::MemoryDomainToken>(0)) {
+        return NN_ERROR() << "V1_3::utils::Buffer::create must have non-zero token";
+    }
+
+    return std::make_shared<const Buffer>(PrivateConstructorTag{}, std::move(buffer), token);
+}
+
+Buffer::Buffer(PrivateConstructorTag /*tag*/, sp<V1_3::IBuffer> buffer,
+               nn::Request::MemoryDomainToken token)
+    : kBuffer(std::move(buffer)), kToken(token) {
+    CHECK(kBuffer != nullptr);
+    CHECK(kToken != static_cast<nn::Request::MemoryDomainToken>(0));
+}
+
+nn::Request::MemoryDomainToken Buffer::getToken() const {
+    return kToken;
+}
+
+nn::GeneralResult<void> Buffer::copyTo(const nn::SharedMemory& dst) const {
+    const auto hidlDst = NN_TRY(convert(dst));
+
+    const auto ret = kBuffer->copyTo(hidlDst);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "IBuffer::copyTo failed with " << toString(status);
+
+    return {};
+}
+
+nn::GeneralResult<void> Buffer::copyFrom(const nn::SharedMemory& src,
+                                         const nn::Dimensions& dimensions) const {
+    const auto hidlSrc = NN_TRY(convert(src));
+    const auto hidlDimensions = hidl_vec<uint32_t>(dimensions);
+
+    const auto ret = kBuffer->copyFrom(hidlSrc, hidlDimensions);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "IBuffer::copyFrom failed with " << toString(status);
+
+    return {};
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Callbacks.cpp b/neuralnetworks/1.3/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..8e9fb83
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Callbacks.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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 "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModelCallback.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Callbacks.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.0/PreparedModel.h>
+#include <nnapi/hal/1.2/Callbacks.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/PreparedModel.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+#include <nnapi/hal/TransferValue.h>
+
+#include <utility>
+
+// 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::V1_3::utils {
+namespace {
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        V1_0::ErrorStatus status, const sp<V1_0::IPreparedModel>& preparedModel) {
+    if (const auto dynamicPreparedModel =
+                V1_3::IPreparedModel::castFrom(preparedModel).withDefault(nullptr)) {
+        const auto currentVersionStatus = NN_TRY(convertFromNonCanonical(status));
+        return V1_3::utils::prepareModelCallback(currentVersionStatus, dynamicPreparedModel);
+    }
+    if (const auto dynamicPreparedModel =
+                V1_2::IPreparedModel::castFrom(preparedModel).withDefault(nullptr)) {
+        return V1_2::utils::prepareModelCallback(status, dynamicPreparedModel);
+    }
+    return V1_0::utils::prepareModelCallback(status, preparedModel);
+}
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+convertExecutionGeneralResultsHelper(const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                     const V1_2::Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::vector<bool>> supportedOperationsCallback(
+        ErrorStatus status, const hidl_vec<bool>& supportedOperations) {
+    HANDLE_HAL_STATUS(status) << "get supported operations failed with " << toString(status);
+    return supportedOperations;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        ErrorStatus status, const sp<IPreparedModel>& preparedModel) {
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    return NN_TRY(PreparedModel::create(preparedModel, /*executeSynchronously=*/true));
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executionCallback(
+        ErrorStatus status, const hidl_vec<V1_2::OutputShape>& outputShapes,
+        const V1_2::Timing& timing) {
+    if (status == ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+        auto canonicalOutputShapes =
+                nn::convert(outputShapes).value_or(std::vector<nn::OutputShape>{});
+        return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+               << "execution failed with " << toString(status);
+    }
+    HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    return hal::utils::makeExecutionFailure(
+            convertExecutionGeneralResultsHelper(outputShapes, timing));
+}
+
+Return<void> PreparedModelCallback::notify(V1_0::ErrorStatus status,
+                                           const sp<V1_0::IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_2(V1_0::ErrorStatus status,
+                                               const sp<V1_2::IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+Return<void> PreparedModelCallback::notify_1_3(ErrorStatus status,
+                                               const sp<IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return Void();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+// ExecutionCallback methods begin here
+
+Return<void> ExecutionCallback::notify(V1_0::ErrorStatus status) {
+    mData.put(V1_0::utils::executionCallback(status));
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_2(V1_0::ErrorStatus status,
+                                           const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                           const V1_2::Timing& timing) {
+    mData.put(V1_2::utils::executionCallback(status, outputShapes, timing));
+    return Void();
+}
+
+Return<void> ExecutionCallback::notify_1_3(ErrorStatus status,
+                                           const hidl_vec<V1_2::OutputShape>& outputShapes,
+                                           const V1_2::Timing& timing) {
+    mData.put(executionCallback(status, outputShapes, timing));
+    return Void();
+}
+
+void ExecutionCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+ExecutionCallback::Data ExecutionCallback::get() {
+    return mData.take();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Conversions.cpp b/neuralnetworks/1.3/utils/src/Conversions.cpp
new file mode 100644
index 0000000..11225cf
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Conversions.cpp
@@ -0,0 +1,731 @@
+/*
+ * 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 "Conversions.h"
+
+#include <android-base/logging.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/1.0/Conversions.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "Utils.h"
+
+namespace {
+
+std::chrono::nanoseconds makeNanosFromUint64(uint64_t nanoseconds) {
+    constexpr auto kMaxCount = std::chrono::nanoseconds::max().count();
+    using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+    const auto count = std::min<CommonType>(kMaxCount, nanoseconds);
+    return std::chrono::nanoseconds{static_cast<std::chrono::nanoseconds::rep>(count)};
+}
+
+uint64_t makeUint64FromNanos(std::chrono::nanoseconds nanoseconds) {
+    if (nanoseconds < std::chrono::nanoseconds::zero()) {
+        return 0;
+    }
+    constexpr auto kMaxCount = std::numeric_limits<uint64_t>::max();
+    using CommonType = std::common_type_t<std::chrono::nanoseconds::rep, uint64_t>;
+    const auto count = std::min<CommonType>(kMaxCount, nanoseconds.count());
+    return static_cast<uint64_t>(count);
+}
+
+template <typename Type>
+constexpr std::underlying_type_t<Type> underlyingType(Type value) {
+    return static_cast<std::underlying_type_t<Type>>(value);
+}
+
+}  // namespace
+
+namespace android::nn {
+namespace {
+
+using hardware::hidl_vec;
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    NN_TRY(hal::V1_3::utils::compliantVersion(canonical));
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const hidl_vec<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
+}
+
+}  // anonymous namespace
+
+GeneralResult<OperandType> unvalidatedConvert(const hal::V1_3::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+GeneralResult<OperationType> unvalidatedConvert(const hal::V1_3::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+GeneralResult<Priority> unvalidatedConvert(const hal::V1_3::Priority& priority) {
+    return static_cast<Priority>(priority);
+}
+
+GeneralResult<Capabilities> unvalidatedConvert(const hal::V1_3::Capabilities& capabilities) {
+    const bool validOperandTypes = std::all_of(
+            capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
+            [](const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
+                return validatedConvert(operandPerformance.type).has_value();
+            });
+    if (!validOperandTypes) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
+                  "Capabilities";
+    }
+
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
+    auto table = NN_TRY(hal::utils::makeGeneralFailure(
+            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+            nn::ErrorStatus::GENERAL_FAILURE));
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = std::move(table),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
+    };
+}
+
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const hal::V1_3::Capabilities::OperandPerformance& operandPerformance) {
+    return Capabilities::OperandPerformance{
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
+    };
+}
+
+GeneralResult<Operation> unvalidatedConvert(const hal::V1_3::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const hal::V1_3::OperandLifeTime& operandLifeTime) {
+    return static_cast<Operand::LifeTime>(operandLifeTime);
+}
+
+GeneralResult<Operand> unvalidatedConvert(const hal::V1_3::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+GeneralResult<Model> unvalidatedConvert(const hal::V1_3::Model& model) {
+    return Model{
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+GeneralResult<Model::Subgraph> unvalidatedConvert(const hal::V1_3::Subgraph& subgraph) {
+    auto operations = NN_TRY(unvalidatedConvert(subgraph.operations));
+
+    // Verify number of consumers.
+    const auto numberOfConsumers =
+            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]) {
+            return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                   << "Invalid numberOfConsumers for operand " << i << ", expected "
+                   << numberOfConsumers[i] << " but found "
+                   << subgraph.operands[i].numberOfConsumers;
+        }
+    }
+
+    return Model::Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
+            .operations = std::move(operations),
+            .inputIndexes = subgraph.inputIndexes,
+            .outputIndexes = subgraph.outputIndexes,
+    };
+}
+
+GeneralResult<BufferDesc> unvalidatedConvert(const hal::V1_3::BufferDesc& bufferDesc) {
+    return BufferDesc{.dimensions = bufferDesc.dimensions};
+}
+
+GeneralResult<BufferRole> unvalidatedConvert(const hal::V1_3::BufferRole& bufferRole) {
+    return BufferRole{
+            .modelIndex = bufferRole.modelIndex,
+            .ioIndex = bufferRole.ioIndex,
+            .probability = bufferRole.frequency,
+    };
+}
+
+GeneralResult<Request> unvalidatedConvert(const hal::V1_3::Request& request) {
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
+    };
+}
+
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const hal::V1_3::Request::MemoryPool& memoryPool) {
+    using Discriminator = hal::V1_3::Request::MemoryPool::hidl_discriminator;
+    switch (memoryPool.getDiscriminator()) {
+        case Discriminator::hidlMemory:
+            return unvalidatedConvert(memoryPool.hidlMemory());
+        case Discriminator::token:
+            return static_cast<Request::MemoryDomainToken>(memoryPool.token());
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid Request::MemoryPool discriminator "
+           << underlyingType(memoryPool.getDiscriminator());
+}
+
+GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+    using Discriminator = hal::V1_3::OptionalTimePoint::hidl_discriminator;
+    switch (optionalTimePoint.getDiscriminator()) {
+        case Discriminator::none:
+            return {};
+        case Discriminator::nanosecondsSinceEpoch: {
+            const auto currentSteadyTime = std::chrono::steady_clock::now();
+            const auto currentBootTime = Clock::now();
+
+            const auto timeSinceEpoch =
+                    makeNanosFromUint64(optionalTimePoint.nanosecondsSinceEpoch());
+            const auto steadyTimePoint = std::chrono::steady_clock::time_point{timeSinceEpoch};
+
+            // Both steadyTimePoint and currentSteadyTime are guaranteed to be non-negative, so this
+            // subtraction will never overflow or underflow.
+            const auto timeRemaining = steadyTimePoint - currentSteadyTime;
+
+            // currentBootTime is guaranteed to be non-negative, so this code only protects against
+            // an overflow.
+            nn::TimePoint bootTimePoint;
+            constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+            constexpr auto kMaxTime = nn::TimePoint::max();
+            if (timeRemaining > kZeroNano && currentBootTime > kMaxTime - timeRemaining) {
+                bootTimePoint = kMaxTime;
+            } else {
+                bootTimePoint = currentBootTime + timeRemaining;
+            }
+
+            constexpr auto kZeroTime = nn::TimePoint{};
+            return std::max(bootTimePoint, kZeroTime);
+        }
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid OptionalTimePoint discriminator "
+           << underlyingType(optionalTimePoint.getDiscriminator());
+}
+
+GeneralResult<OptionalDuration> unvalidatedConvert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    using Discriminator = hal::V1_3::OptionalTimeoutDuration::hidl_discriminator;
+    switch (optionalTimeoutDuration.getDiscriminator()) {
+        case Discriminator::none:
+            return {};
+        case Discriminator::nanoseconds:
+            return Duration(optionalTimeoutDuration.nanoseconds());
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid OptionalTimeoutDuration discriminator "
+           << underlyingType(optionalTimeoutDuration.getDiscriminator());
+}
+
+GeneralResult<ErrorStatus> unvalidatedConvert(const hal::V1_3::ErrorStatus& status) {
+    switch (status) {
+        case hal::V1_3::ErrorStatus::NONE:
+        case hal::V1_3::ErrorStatus::DEVICE_UNAVAILABLE:
+        case hal::V1_3::ErrorStatus::GENERAL_FAILURE:
+        case hal::V1_3::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case hal::V1_3::ErrorStatus::INVALID_ARGUMENT:
+        case hal::V1_3::ErrorStatus::MISSED_DEADLINE_TRANSIENT:
+        case hal::V1_3::ErrorStatus::MISSED_DEADLINE_PERSISTENT:
+        case hal::V1_3::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT:
+        case hal::V1_3::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
+            return static_cast<ErrorStatus>(status);
+    }
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "Invalid ErrorStatus " << underlyingType(status);
+}
+
+GeneralResult<Priority> convert(const hal::V1_3::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+GeneralResult<Capabilities> convert(const hal::V1_3::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<Model> convert(const hal::V1_3::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<BufferDesc> convert(const hal::V1_3::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+GeneralResult<Request> convert(const hal::V1_3::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<OptionalTimePoint> convert(const hal::V1_3::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+GeneralResult<OptionalDuration> convert(
+        const hal::V1_3::OptionalTimeoutDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+GeneralResult<ErrorStatus> convert(const hal::V1_3::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+GeneralResult<SharedHandle> convert(const hardware::hidl_handle& handle) {
+    return validatedConvert(handle);
+}
+
+GeneralResult<std::vector<BufferRole>> convert(
+        const hardware::hidl_vec<hal::V1_3::BufferRole>& bufferRoles) {
+    return validatedConvert(bufferRoles);
+}
+
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using utils::unvalidatedConvert;
+
+nn::GeneralResult<V1_0::PerformanceInfo> unvalidatedConvert(
+        const nn::Capabilities::PerformanceInfo& performanceInfo) {
+    return V1_0::utils::unvalidatedConvert(performanceInfo);
+}
+
+nn::GeneralResult<V1_0::DataLocation> unvalidatedConvert(const nn::DataLocation& dataLocation) {
+    return V1_0::utils::unvalidatedConvert(dataLocation);
+}
+
+nn::GeneralResult<hidl_vec<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return V1_0::utils::unvalidatedConvert(operandValues);
+}
+
+nn::GeneralResult<hidl_handle> unvalidatedConvert(const nn::SharedHandle& handle) {
+    return V1_2::utils::unvalidatedConvert(handle);
+}
+
+nn::GeneralResult<hidl_memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    return V1_0::utils::unvalidatedConvert(memory);
+}
+
+nn::GeneralResult<V1_0::RequestArgument> unvalidatedConvert(const nn::Request::Argument& argument) {
+    return V1_0::utils::unvalidatedConvert(argument);
+}
+
+nn::GeneralResult<V1_2::Operand::ExtraParams> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
+    return V1_2::utils::unvalidatedConvert(extraParams);
+}
+
+nn::GeneralResult<V1_2::Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameAndPrefix) {
+    return V1_2::utils::unvalidatedConvert(extensionNameAndPrefix);
+}
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(unvalidatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedMemory& memory) {
+    Request::MemoryPool ret;
+    ret.hidlMemory(NN_TRY(unvalidatedConvert(memory)));
+    return ret;
+}
+
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::Request::MemoryDomainToken& token) {
+    Request::MemoryPool ret;
+    ret.token(underlyingType(token));
+    return ret;
+}
+
+nn::GeneralResult<Request::MemoryPool> makeMemoryPool(const nn::SharedBuffer& /*buffer*/) {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "Unable to make memory pool from IBuffer";
+}
+
+using utils::unvalidatedConvert;
+
+template <typename Type>
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<hidl_vec<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    hidl_vec<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+}  // anonymous namespace
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+    return static_cast<OperandType>(operandType);
+}
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+    return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
+    return static_cast<Priority>(priority);
+}
+
+nn::GeneralResult<Capabilities> unvalidatedConvert(const nn::Capabilities& capabilities) {
+    std::vector<nn::Capabilities::OperandPerformance> operandPerformance;
+    operandPerformance.reserve(capabilities.operandPerformance.asVector().size());
+    std::copy_if(capabilities.operandPerformance.asVector().begin(),
+                 capabilities.operandPerformance.asVector().end(),
+                 std::back_inserter(operandPerformance),
+                 [](const nn::Capabilities::OperandPerformance& operandPerformance) {
+                     return compliantVersion(operandPerformance.type).has_value();
+                 });
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = NN_TRY(unvalidatedConvert(operandPerformance)),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
+    };
+}
+
+nn::GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const nn::Capabilities::OperandPerformance& operandPerformance) {
+    return Capabilities::OperandPerformance{
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
+    };
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = operation.inputs,
+            .outputs = operation.outputs,
+    };
+}
+
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(
+        const nn::Operand::LifeTime& operandLifeTime) {
+    if (operandLifeTime == nn::Operand::LifeTime::POINTER) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+    return static_cast<OperandLifeTime>(operandLifeTime);
+}
+
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = operand.dimensions,
+            .numberOfConsumers = 0,
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+    if (!hal::utils::hasNoPointerData(model)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Model cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    return Model{
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) {
+    auto operands = NN_TRY(unvalidatedConvert(subgraph.operands));
+
+    // Update number of consumers.
+    const auto numberOfConsumers =
+            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];
+    }
+
+    return Subgraph{
+            .operands = std::move(operands),
+            .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
+            .inputIndexes = subgraph.inputIndexes,
+            .outputIndexes = subgraph.outputIndexes,
+    };
+}
+
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) {
+    return BufferDesc{.dimensions = bufferDesc.dimensions};
+}
+
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) {
+    return BufferRole{
+            .modelIndex = bufferRole.modelIndex,
+            .ioIndex = bufferRole.ioIndex,
+            .frequency = bufferRole.probability,
+    };
+}
+
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
+    if (!hal::utils::hasNoPointerData(request)) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
+    };
+}
+
+nn::GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const nn::Request::MemoryPool& memoryPool) {
+    return std::visit([](const auto& o) { return makeMemoryPool(o); }, memoryPool);
+}
+
+nn::GeneralResult<OptionalTimePoint> unvalidatedConvert(
+        const nn::OptionalTimePoint& optionalTimePoint) {
+    const auto currentSteadyTime = std::chrono::steady_clock::now();
+    const auto currentBootTime = nn::Clock::now();
+
+    OptionalTimePoint ret;
+    if (optionalTimePoint.has_value()) {
+        const auto bootTimePoint = optionalTimePoint.value();
+
+        if (bootTimePoint < nn::TimePoint{}) {
+            return NN_ERROR() << "Trying to cast invalid time point";
+        }
+
+        // Both bootTimePoint and currentBootTime are guaranteed to be non-negative, so this
+        // subtraction will never overflow or underflow.
+        const auto timeRemaining = bootTimePoint - currentBootTime;
+
+        // currentSteadyTime is guaranteed to be non-negative, so this code only protects against an
+        // overflow.
+        std::chrono::steady_clock::time_point steadyTimePoint;
+        constexpr auto kZeroNano = std::chrono::nanoseconds::zero();
+        constexpr auto kMaxTime = std::chrono::steady_clock::time_point::max();
+        if (timeRemaining > kZeroNano && currentSteadyTime > kMaxTime - timeRemaining) {
+            steadyTimePoint = kMaxTime;
+        } else {
+            steadyTimePoint = currentSteadyTime + timeRemaining;
+        }
+
+        const uint64_t count = makeUint64FromNanos(steadyTimePoint.time_since_epoch());
+        ret.nanosecondsSinceEpoch(count);
+    }
+    return ret;
+}
+
+nn::GeneralResult<OptionalTimeoutDuration> unvalidatedConvert(
+        const nn::OptionalDuration& optionalTimeoutDuration) {
+    OptionalTimeoutDuration ret;
+    if (optionalTimeoutDuration.has_value()) {
+        const auto count = optionalTimeoutDuration.value().count();
+        ret.nanoseconds(count);
+    }
+    return ret;
+}
+
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus) {
+    switch (errorStatus) {
+        case nn::ErrorStatus::NONE:
+        case nn::ErrorStatus::DEVICE_UNAVAILABLE:
+        case nn::ErrorStatus::GENERAL_FAILURE:
+        case nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case nn::ErrorStatus::INVALID_ARGUMENT:
+        case nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT:
+        case nn::ErrorStatus::MISSED_DEADLINE_PERSISTENT:
+        case nn::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT:
+        case nn::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
+            return static_cast<ErrorStatus>(errorStatus);
+        default:
+            return ErrorStatus::GENERAL_FAILURE;
+    }
+}
+
+nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+nn::GeneralResult<Capabilities> convert(const nn::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<OptionalTimePoint> convert(const nn::OptionalTimePoint& optionalTimePoint) {
+    return validatedConvert(optionalTimePoint);
+}
+
+nn::GeneralResult<OptionalTimeoutDuration> convert(
+        const nn::OptionalDuration& optionalTimeoutDuration) {
+    return validatedConvert(optionalTimeoutDuration);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+nn::GeneralResult<hidl_handle> convert(const nn::SharedHandle& handle) {
+    return validatedConvert(handle);
+}
+
+nn::GeneralResult<hidl_memory> convert(const nn::SharedMemory& memory) {
+    return validatedConvert(memory);
+}
+
+nn::GeneralResult<hidl_vec<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
+    return validatedConvert(bufferRoles);
+}
+
+nn::GeneralResult<V1_0::DeviceStatus> convert(const nn::DeviceStatus& deviceStatus) {
+    return V1_2::utils::convert(deviceStatus);
+}
+
+nn::GeneralResult<V1_1::ExecutionPreference> convert(
+        const nn::ExecutionPreference& executionPreference) {
+    return V1_2::utils::convert(executionPreference);
+}
+
+nn::GeneralResult<hidl_vec<V1_2::Extension>> convert(const std::vector<nn::Extension>& extensions) {
+    return V1_2::utils::convert(extensions);
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convert(const std::vector<nn::SharedHandle>& handles) {
+    return V1_2::utils::convert(handles);
+}
+
+nn::GeneralResult<hidl_vec<V1_2::OutputShape>> convert(
+        const std::vector<nn::OutputShape>& outputShapes) {
+    return V1_2::utils::convert(outputShapes);
+}
+
+nn::GeneralResult<V1_2::DeviceType> convert(const nn::DeviceType& deviceType) {
+    return V1_2::utils::convert(deviceType);
+}
+
+nn::GeneralResult<V1_2::MeasureTiming> convert(const nn::MeasureTiming& measureTiming) {
+    return V1_2::utils::convert(measureTiming);
+}
+
+nn::GeneralResult<V1_2::Timing> convert(const nn::Timing& timing) {
+    return V1_2::utils::convert(timing);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Device.cpp b/neuralnetworks/1.3/utils/src/Device.cpp
new file mode 100644
index 0000000..d710b85
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Device.cpp
@@ -0,0 +1,253 @@
+/*
+ * 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 "Callbacks.h"
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.1/Conversions.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/Device.h>
+#include <nnapi/hal/1.2/Utils.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <any>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// 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::V1_3::utils {
+namespace {
+
+nn::GeneralResult<hidl_vec<sp<IPreparedModel>>> convert(
+        const std::vector<nn::SharedPreparedModel>& preparedModels) {
+    hidl_vec<sp<IPreparedModel>> hidlPreparedModels(preparedModels.size());
+    for (size_t i = 0; i < preparedModels.size(); ++i) {
+        std::any underlyingResource = preparedModels[i]->getUnderlyingResource();
+        if (const auto* hidlPreparedModel =
+                    std::any_cast<sp<IPreparedModel>>(&underlyingResource)) {
+            hidlPreparedModels[i] = *hidlPreparedModel;
+        } else {
+            return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+                   << "Unable to convert from nn::IPreparedModel to V1_3::IPreparedModel";
+        }
+    }
+    return hidlPreparedModels;
+}
+
+nn::GeneralResult<nn::Capabilities> capabilitiesCallback(ErrorStatus status,
+                                                         const Capabilities& capabilities) {
+    HANDLE_HAL_STATUS(status) << "getting capabilities failed with " << toString(status);
+    return nn::convert(capabilities);
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(V1_3::IDevice* device) {
+    CHECK(device != nullptr);
+
+    auto cb = hal::utils::CallbackValue(capabilitiesCallback);
+
+    const auto ret = device->getCapabilities_1_3(cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedBuffer> allocationCallback(ErrorStatus status,
+                                                       const sp<IBuffer>& buffer, uint32_t token) {
+    HANDLE_HAL_STATUS(status) << "IDevice::allocate failed with " << toString(status);
+    return Buffer::create(buffer, static_cast<nn::Request::MemoryDomainToken>(token));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(std::string name,
+                                                                sp<V1_3::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "V1_3::utils::Device::create must have non-null device";
+    }
+
+    auto versionString = NN_TRY(V1_2::utils::getVersionStringFrom(device.get()));
+    const auto deviceType = NN_TRY(V1_2::utils::getDeviceTypeFrom(device.get()));
+    auto extensions = NN_TRY(V1_2::utils::getSupportedExtensionsFrom(device.get()));
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+    const auto numberOfCacheFilesNeeded =
+            NN_TRY(V1_2::utils::getNumberOfCacheFilesNeededFrom(device.get()));
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(device));
+    return std::make_shared<const Device>(
+            PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+            std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+            std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+               nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+               nn::Capabilities capabilities,
+               std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded, sp<V1_3::IDevice> device,
+               hal::utils::DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kDeviceType(deviceType),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_R;
+}
+
+nn::DeviceType Device::getType() const {
+    return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = kDevice->ping();
+    HANDLE_TRANSPORT_FAILURE(ret);
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+
+    auto cb = hal::utils::CallbackValue(supportedOperationsCallback);
+
+    const auto ret = kDevice->getSupportedOperations_1_3(hidlModel, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto hidlModel = NN_TRY(convert(modelInShared));
+    const auto hidlPreference = NN_TRY(convert(preference));
+    const auto hidlPriority = NN_TRY(convert(priority));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = V1_2::utils::CacheToken{token};
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret =
+            kDevice->prepareModel_1_3(hidlModel, hidlPreference, hidlPriority, hidlDeadline,
+                                      hidlModelCache, hidlDataCache, hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlModelCache = NN_TRY(convert(modelCache));
+    const auto hidlDataCache = NN_TRY(convert(dataCache));
+    const auto hidlToken = V1_2::utils::CacheToken{token};
+
+    const auto cb = sp<PreparedModelCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModelFromCache_1_3(hidlDeadline, hidlModelCache, hidlDataCache,
+                                                        hidlToken, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    HANDLE_HAL_STATUS(status) << "model preparation from cache failed with " << toString(status);
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        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 hidlDesc = NN_TRY(convert(desc));
+    const auto hidlPreparedModels = NN_TRY(convert(preparedModels));
+    const auto hidlInputRoles = NN_TRY(convert(inputRoles));
+    const auto hidlOutputRoles = NN_TRY(convert(outputRoles));
+
+    auto cb = hal::utils::CallbackValue(allocationCallback);
+
+    const auto ret =
+            kDevice->allocate(hidlDesc, hidlPreparedModels, hidlInputRoles, hidlOutputRoles, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Execution.cpp b/neuralnetworks/1.3/utils/src/Execution.cpp
new file mode 100644
index 0000000..3d17cc3
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Execution.cpp
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+#include "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// 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::V1_3::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+        OptionalTimeoutDuration loopTimeoutDuration) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_3::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure,
+                                             std::move(loopTimeoutDuration));
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation, V1_2::MeasureTiming measure,
+                     OptionalTimeoutDuration loopTimeoutDuration)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(std::move(loopTimeoutDuration)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kPreparedModel->executeInternal(kRequest, kMeasure, hidlDeadline, kLoopTimeoutDuration,
+                                           kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return kPreparedModel->executeFencedInternal(kRequest, hidlWaitFor, kMeasure, hidlDeadline,
+                                                 kLoopTimeoutDuration,
+                                                 hidlTimeoutDurationAfterFence, kRelocation);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/PreparedModel.cpp b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..1623de5
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/PreparedModel.cpp
@@ -0,0 +1,270 @@
+/*
+ * 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 "Callbacks.h"
+#include "Conversions.h"
+#include "Execution.h"
+#include "Utils.h"
+
+#include <android/hardware/neuralnetworks/1.0/types.h>
+#include <android/hardware/neuralnetworks/1.1/types.h>
+#include <android/hardware/neuralnetworks/1.2/types.h>
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <android/hardware/neuralnetworks/1.3/types.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.2/Conversions.h>
+#include <nnapi/hal/1.2/ExecutionBurstController.h>
+#include <nnapi/hal/1.2/ExecutionBurstUtils.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// 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::V1_3::utils {
+namespace {
+
+nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionCallbackResults(
+        ErrorStatus status, const V1_2::Timing& timingLaunched, const V1_2::Timing& timingFenced) {
+    HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+    return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> fencedExecutionCallback(
+        ErrorStatus status, const hidl_handle& syncFence,
+        const sp<IFencedExecutionCallback>& callback) {
+    HANDLE_HAL_STATUS(status) << "fenced execution failed with " << toString(status);
+
+    auto resultSyncFence = nn::SyncFence::createAsSignaled();
+    if (syncFence.getNativeHandle() != nullptr) {
+        auto sharedHandle = NN_TRY(nn::convert(syncFence));
+        resultSyncFence = NN_TRY(hal::utils::makeGeneralFailure(
+                nn::SyncFence::create(std::move(sharedHandle)), nn::ErrorStatus::GENERAL_FAILURE));
+    }
+
+    if (callback == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
+    }
+
+    // Create callback which can be used to retrieve the execution error status and timings.
+    nn::ExecuteFencedInfoCallback resultCallback =
+            [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
+        auto cb = hal::utils::CallbackValue(convertFencedExecutionCallbackResults);
+
+        const auto ret = callback->getExecutionInfo(cb);
+        HANDLE_TRANSPORT_FAILURE(ret);
+
+        return cb.take();
+    };
+
+    return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        sp<V1_3::IPreparedModel> preparedModel, bool executeSynchronously) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "V1_3::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    auto deathHandler = NN_TRY(hal::utils::DeathHandler::create(preparedModel));
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, executeSynchronously,
+                                                 std::move(preparedModel), std::move(deathHandler));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/, bool executeSynchronously,
+                             sp<V1_3::IPreparedModel> preparedModel,
+                             hal::utils::DeathHandler deathHandler)
+    : kExecuteSynchronously(executeSynchronously),
+      kPreparedModel(std::move(preparedModel)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeSynchronously(const Request& request, V1_2::MeasureTiming measure,
+                                    const OptionalTimePoint& deadline,
+                                    const OptionalTimeoutDuration& loopTimeoutDuration) const {
+    auto cb = hal::utils::CallbackValue(executionCallback);
+
+    const auto ret = kPreparedModel->executeSynchronously_1_3(request, measure, deadline,
+                                                              loopTimeoutDuration, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+
+    return cb.take();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeAsynchronously(const Request& request, V1_2::MeasureTiming measure,
+                                     const OptionalTimePoint& deadline,
+                                     const OptionalTimeoutDuration& loopTimeoutDuration) const {
+    const auto cb = sp<ExecutionCallback>::make();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret =
+            kPreparedModel->execute_1_3(request, measure, deadline, loopTimeoutDuration, cb);
+    const auto status = HANDLE_TRANSPORT_FAILURE(ret);
+    if (status != ErrorStatus::OUTPUT_INSUFFICIENT_SIZE) {
+        HANDLE_HAL_STATUS(status) << "execution failed with " << toString(status);
+    }
+
+    return cb->get();
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    const auto hidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto hidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+    const auto hidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto hidlLoopTimeoutDuration =
+            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+
+    return executeInternal(hidlRequest, hidlMeasure, hidlDeadline, hidlLoopTimeoutDuration,
+                           relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, V1_2::MeasureTiming measure,
+                               const OptionalTimePoint& deadline,
+                               const OptionalTimeoutDuration& loopTimeoutDuration,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    auto result = kExecuteSynchronously
+                          ? executeSynchronously(request, measure, deadline, loopTimeoutDuration)
+                          : executeAsynchronously(request, measure, deadline, loopTimeoutDuration);
+    auto [outputShapes, timing] = NN_TRY(std::move(result));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return std::make_pair(std::move(outputShapes), timing);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+                             nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+                             const nn::OptionalDuration& loopTimeoutDuration,
+                             const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    const auto hidlRequest = NN_TRY(convert(requestInShared));
+    const auto hidlWaitFor = NN_TRY(hal::utils::convertSyncFences(waitFor));
+    const auto hidlMeasure = NN_TRY(convert(measure));
+    const auto hidlDeadline = NN_TRY(convert(deadline));
+    const auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    const auto hidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+
+    return executeFencedInternal(hidlRequest, hidlWaitFor, hidlMeasure, hidlDeadline,
+                                 hidlLoopTimeoutDuration, hidlTimeoutDurationAfterFence,
+                                 relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request, const hidl_vec<hidl_handle>& waitFor,
+                                     V1_2::MeasureTiming measure, const OptionalTimePoint& deadline,
+                                     const OptionalTimeoutDuration& loopTimeoutDuration,
+                                     const OptionalTimeoutDuration& timeoutDurationAfterFence,
+                                     const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    auto cb = hal::utils::CallbackValue(fencedExecutionCallback);
+
+    const auto ret =
+            kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+                                          timeoutDurationAfterFence, cb);
+    HANDLE_TRANSPORT_FAILURE(ret);
+    auto [syncFence, callback] = NN_TRY(cb.take());
+
+    // If executeFenced required the request memory to be moved into shared memory, block here until
+    // the fenced execution has completed and flush the memory back.
+    if (relocation.output) {
+        const auto state = syncFence.syncWait({});
+        if (state != nn::SyncFence::FenceState::SIGNALED) {
+            return NN_ERROR() << "syncWait failed with " << state;
+        }
+        relocation.output->flush();
+    }
+
+    return std::make_pair(std::move(syncFence), std::move(callback));
+}
+
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kMinMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto hidlRequest = NN_TRY(convert(requestInShared));
+    auto hidlMeasure = NN_TRY(convert(measure));
+    auto hidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    return Execution::create(shared_from_this(), std::move(hidlRequest), std::move(relocation),
+                             hidlMeasure, std::move(hidlLoopTimeoutDuration));
+}
+
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+    auto self = shared_from_this();
+    auto fallback = [preparedModel = std::move(self)](
+                            const nn::Request& request, nn::MeasureTiming measure,
+                            const nn::OptionalTimePoint& deadline,
+                            const nn::OptionalDuration& loopTimeoutDuration)
+            -> nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> {
+        return preparedModel->execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    const auto pollingTimeWindow = V1_2::utils::getBurstControllerPollingTimeWindow();
+    return V1_2::utils::ExecutionBurstController::create(shared_from_this(), kPreparedModel,
+                                                         pollingTimeWindow);
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    sp<V1_3::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/src/Service.cpp b/neuralnetworks/1.3/utils/src/Service.cpp
new file mode 100644
index 0000000..62887fb
--- /dev/null
+++ b/neuralnetworks/1.3/utils/src/Service.cpp
@@ -0,0 +1,41 @@
+/*
+ * 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 "Service.h"
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+#include "Device.h"
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& name) {
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [name](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        auto service = blocking ? IDevice::getService(name) : IDevice::tryGetService(name);
+        if (service == nullptr) {
+            return NN_ERROR() << (blocking ? "getService" : "tryGetService") << " returned nullptr";
+        }
+        return Device::create(name, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/test/BufferTest.cpp b/neuralnetworks/1.3/utils/test/BufferTest.cpp
new file mode 100644
index 0000000..d892b87
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/BufferTest.cpp
@@ -0,0 +1,208 @@
+/*
+ * 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 "MockBuffer.h"
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.3/Buffer.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+const auto kMemory = nn::createSharedMemory(4).value();
+const sp<V1_3::IBuffer> kInvalidBuffer;
+constexpr auto kInvalidToken = nn::Request::MemoryDomainToken{0};
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+std::function<hardware::Return<V1_3::ErrorStatus>()> makeFunctionReturn(V1_3::ErrorStatus status) {
+    return [status]() -> hardware::Return<V1_3::ErrorStatus> { return status; };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeSuccessful = makeFunctionReturn(V1_3::ErrorStatus::NONE);
+const auto makeGeneralError = makeFunctionReturn(V1_3::ErrorStatus::GENERAL_FAILURE);
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(BufferTest, invalidBuffer) {
+    // run test
+    const auto result = Buffer::create(kInvalidBuffer, kToken);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, invalidToken) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+
+    // run test
+    const auto result = Buffer::create(mockBuffer, kInvalidToken);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, create) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+
+    // run test
+    const auto token = buffer->getToken();
+
+    // verify result
+    EXPECT_EQ(token, kToken);
+}
+
+TEST(BufferTest, copyTo) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeSuccessful));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    EXPECT_TRUE(result.has_value()) << result.error().message;
+}
+
+TEST(BufferTest, copyToError) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralError));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToTransportFailure) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToDeadObject) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(BufferTest, copyFrom) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(InvokeWithoutArgs(makeSuccessful));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value());
+}
+
+TEST(BufferTest, copyFromError) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralError));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromTransportFailure) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromDeadObject) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/test/DeviceTest.cpp b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..2d1b2f2
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/DeviceTest.cpp
@@ -0,0 +1,968 @@
+/*
+ * 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 "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.3/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const nn::Model kSimpleModel = {
+        .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+                              {.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+                 .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+                 .inputIndexes = {0},
+                 .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const sp<V1_3::IDevice> kInvalidDevice;
+constexpr V1_0::PerformanceInfo kNoPerformanceInfo = {
+        .execTime = std::numeric_limits<float>::max(),
+        .powerUsage = std::numeric_limits<float>::max()};
+
+template <typename... Args>
+auto makeCallbackReturn(Args&&... args) {
+    return [argPack = std::make_tuple(std::forward<Args>(args)...)](const auto& cb) {
+        std::apply(cb, argPack);
+        return Void();
+    };
+}
+
+sp<MockDevice> createMockDevice() {
+    const auto mockDevice = MockDevice::create();
+
+    // Setup default actions for each relevant call.
+    const auto getVersionString_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, kName);
+    const auto getType_ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, V1_2::DeviceType::OTHER);
+    const auto getSupportedExtensions_ret =
+            makeCallbackReturn(V1_0::ErrorStatus::NONE, hidl_vec<V1_2::Extension>{});
+    const auto getNumberOfCacheFilesNeeded_ret = makeCallbackReturn(
+            V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+    const auto getCapabilities_ret = makeCallbackReturn(
+            V1_3::ErrorStatus::NONE,
+            V1_3::Capabilities{
+                    .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+                    .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+                    .ifPerformance = kNoPerformanceInfo,
+                    .whilePerformance = kNoPerformanceInfo,
+            });
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getVersionString(_)).WillByDefault(Invoke(getVersionString_ret));
+    ON_CALL(*mockDevice, getType(_)).WillByDefault(Invoke(getType_ret));
+    ON_CALL(*mockDevice, getSupportedExtensions(_))
+            .WillByDefault(Invoke(getSupportedExtensions_ret));
+    ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .WillByDefault(Invoke(getNumberOfCacheFilesNeeded_ret));
+    ON_CALL(*mockDevice, getCapabilities_1_3(_)).WillByDefault(Invoke(getCapabilities_ret));
+
+    // Ensure that older calls are not used.
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getCapabilities_1_1(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getCapabilities_1_2(_)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_1(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel_1_1(_, _, _)).Times(0);
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_2(_, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModel_1_2(_, _, _, _, _, _)).Times(0);
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _)).Times(0);
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getType(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+auto makePreparedModelReturn(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus,
+                             const sp<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const V1_3::Model& /*model*/, V1_1::ExecutionPreference /*preference*/,
+                   V1_3::Priority /*priority*/, const V1_3::OptionalTimePoint& /*deadline*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+                   const CacheToken& /*token*/, const sp<V1_3::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_3::ErrorStatus> {
+        cb->notify_1_3(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+auto makePreparedModelFromCacheReturn(V1_3::ErrorStatus launchStatus,
+                                      V1_3::ErrorStatus returnStatus,
+                                      const sp<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const V1_3::OptionalTimePoint& /*deadline*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*modelCache*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*dataCache*/,
+                   const CacheToken& /*token*/, const sp<V1_3::IPreparedModelCallback>& cb)
+                   -> hardware::Return<V1_3::ErrorStatus> {
+        cb->notify_1_3(returnStatus, preparedModel).isOk();
+        return launchStatus;
+    };
+}
+auto makeAllocateReturn(ErrorStatus status, const sp<MockBuffer>& buffer, uint32_t token) {
+    return [status, buffer, token](
+                   const V1_3::BufferDesc& /*desc*/,
+                   const hardware::hidl_vec<sp<V1_3::IPreparedModel>>& /*preparedModels*/,
+                   const hardware::hidl_vec<V1_3::BufferRole>& /*inputRoles*/,
+                   const hardware::hidl_vec<V1_3::BufferRole>& /*outputRoles*/,
+                   const V1_3::IDevice::allocate_cb& cb) -> hardware::Return<void> {
+        cb(status, buffer, token);
+        return hardware::Void();
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(DeviceTest, invalidName) {
+    // run test
+    const auto device = MockDevice::create();
+    const auto result = Device::create(kInvalidName, device);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+    // run test
+    const auto result = Device::create(kName, kInvalidDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getVersionStringError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, "");
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getTypeError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret =
+            makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, V1_2::DeviceType::OTHER);
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedExtensionsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret =
+            makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE, hidl_vec<V1_2::Extension>{});
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::GENERAL_FAILURE,
+                                        nn::kMaxNumberOfCacheFiles, nn::kMaxNumberOfCacheFiles);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles + 1,
+                                        nn::kMaxNumberOfCacheFiles);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(V1_0::ErrorStatus::NONE, nn::kMaxNumberOfCacheFiles,
+                                        nn::kMaxNumberOfCacheFiles + 1);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = makeCallbackReturn(
+            V1_3::ErrorStatus::GENERAL_FAILURE,
+            V1_3::Capabilities{
+                    .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+                    .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+                    .ifPerformance = kNoPerformanceInfo,
+                    .whilePerformance = kNoPerformanceInfo,
+            });
+    EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_3(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities_1_3(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, linkToDeathError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto& name = device->getName();
+
+    // verify result
+    EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify result
+    EXPECT_EQ(featureLevel, nn::Version::ANDROID_R);
+}
+
+TEST(DeviceTest, getCachedData) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getCapabilities_1_3(_)).Times(1);
+
+    const auto result = Device::create(kName, mockDevice);
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& device = result.value();
+
+    // run test and verify results
+    EXPECT_EQ(device->getVersionString(), device->getVersionString());
+    EXPECT_EQ(device->getType(), device->getType());
+    EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+    EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+    EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, wait) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto ret = []() -> Return<void> { return {}; };
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(DeviceTest, waitTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, waitDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, ping()).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& model, const auto& cb) {
+        cb(V1_3::ErrorStatus::NONE, std::vector<bool>(model.main.operations.size(), true));
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& supportedOperations = result.value();
+    EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+    EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [](const auto& /*model*/, const auto& cb) {
+        cb(V1_3::ErrorStatus::GENERAL_FAILURE, {});
+        return hardware::Void();
+    };
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _)).Times(1).WillOnce(Invoke(ret));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations_1_3(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+                                                     V1_3::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                     V1_3::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+                                                     V1_3::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(V1_3::ErrorStatus::NONE,
+                                                     V1_3::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_3::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_3::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModel_1_3(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCache) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                              V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                              nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(V1_3::ErrorStatus::NONE,
+                                                              V1_3::ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&mockDevice]() -> hardware::Return<V1_3::ErrorStatus> {
+        mockDevice->simulateCrash();
+        return V1_3::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockDevice, prepareModelFromCache_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, allocate) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockBuffer = MockBuffer::create();
+    constexpr uint32_t token = 1;
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeAllocateReturn(ErrorStatus::NONE, mockBuffer, token)));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, allocateError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeAllocateReturn(ErrorStatus::GENERAL_FAILURE, nullptr, 0)));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/utils/test/MockBuffer.h b/neuralnetworks/1.3/utils/test/MockBuffer.h
new file mode 100644
index 0000000..a67c5f6
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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_1_3_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
+
+#include <android/hardware/neuralnetworks/1.3/IBuffer.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockBuffer final : public IBuffer {
+  public:
+    static sp<MockBuffer> create();
+
+    // V1_3 methods below.
+    MOCK_METHOD(Return<V1_3::ErrorStatus>, copyTo, (const hidl_memory& dst), (override));
+    MOCK_METHOD(Return<V1_3::ErrorStatus>, copyFrom,
+                (const hidl_memory& src, const hidl_vec<uint32_t>& dimensions), (override));
+};
+
+inline sp<MockBuffer> MockBuffer::create() {
+    return sp<MockBuffer>::make();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/1.3/utils/test/MockBurstContext.h b/neuralnetworks/1.3/utils/test/MockBurstContext.h
new file mode 100644
index 0000000..e102b46
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockBurstContext.h
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
+
+#include <android/hardware/neuralnetworks/1.2/IBurstContext.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockBurstContext final : public V1_2::IBurstContext {
+  public:
+    // V1_2 methods below.
+    MOCK_METHOD(Return<void>, freeMemory, (int32_t slot), (override));
+};
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_BURST_CONTEXT_H
diff --git a/neuralnetworks/1.3/utils/test/MockDevice.h b/neuralnetworks/1.3/utils/test/MockDevice.h
new file mode 100644
index 0000000..b79037f
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockDevice.h
@@ -0,0 +1,139 @@
+/*
+ * 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_1_3_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
+
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+using CacheToken =
+        hidl_array<uint8_t, static_cast<uint32_t>(V1_2::Constant::BYTE_SIZE_OF_CACHE_TOKEN)>;
+
+class MockDevice final : public IDevice {
+  public:
+    static sp<MockDevice> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient, uint64_t /*cookie*/);
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities, (getCapabilities_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations,
+                (const V1_0::Model& model, getSupportedOperations_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel,
+                (const V1_0::Model& model, const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<V1_0::DeviceStatus>, getStatus, (), (override));
+
+    // V1_1 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities_1_1, (getCapabilities_1_1_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_1,
+                (const V1_1::Model& model, getSupportedOperations_1_1_cb cb), (override));
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, prepareModel_1_1,
+                (const V1_1::Model& model, V1_1::ExecutionPreference preference,
+                 const sp<V1_0::IPreparedModelCallback>& callback),
+                (override));
+
+    // V1_2 methods below.
+    MOCK_METHOD(Return<void>, getVersionString, (getVersionString_cb cb), (override));
+    MOCK_METHOD(Return<void>, getType, (getType_cb cb), (override));
+    MOCK_METHOD(Return<void>, getCapabilities_1_2, (getCapabilities_1_2_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedExtensions, (getSupportedExtensions_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_2,
+                (const V1_2::Model& model, getSupportedOperations_1_2_cb cb), (override));
+    MOCK_METHOD(Return<void>, getNumberOfCacheFilesNeeded, (getNumberOfCacheFilesNeeded_cb cb),
+                (override));
+    MOCK_METHOD(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));
+    MOCK_METHOD(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));
+
+    // V1_3 methods below.
+    MOCK_METHOD(Return<void>, getCapabilities_1_3, (getCapabilities_1_3_cb cb), (override));
+    MOCK_METHOD(Return<void>, getSupportedOperations_1_3,
+                (const V1_3::Model& model, getSupportedOperations_1_3_cb cb), (override));
+    MOCK_METHOD(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));
+    MOCK_METHOD(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));
+    MOCK_METHOD(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));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockDevice> MockDevice::create() {
+    auto mockDevice = sp<MockDevice>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+inline Return<bool> MockDevice::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                            uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockDevice::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::Device will not use the `cookie` or `who` arguments, so we pass in 0
+    // and nullptr for these arguments instead. Normally, they are used by the hidl_death_recipient
+    // to determine which object is dead. However, the utils::Device code only pairs a single death
+    // recipient with a single HIDL interface object, so these arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
new file mode 100644
index 0000000..04c0a92
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockFencedExecutionCallback.h
@@ -0,0 +1,42 @@
+/*
+ * 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_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockFencedExecutionCallback final : public IFencedExecutionCallback {
+  public:
+    static sp<MockFencedExecutionCallback> create();
+
+    // V1_3 methods below.
+    MOCK_METHOD(Return<void>, getExecutionInfo, (IFencedExecutionCallback::getExecutionInfo_cb cb),
+                (override));
+};
+
+inline sp<MockFencedExecutionCallback> MockFencedExecutionCallback::create() {
+    return sp<MockFencedExecutionCallback>::make();
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/1.3/utils/test/MockPreparedModel.h b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..ef64fa4
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/MockPreparedModel.h
@@ -0,0 +1,121 @@
+/*
+ * 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_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
+
+#include <android/hardware/neuralnetworks/1.3/IPreparedModel.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+
+class MockPreparedModel final : public IPreparedModel {
+  public:
+    static sp<MockPreparedModel> create();
+
+    // IBase methods below.
+    MOCK_METHOD(Return<void>, ping, (), (override));
+    MOCK_METHOD(Return<bool>, linkToDeathRet, ());
+    Return<bool> linkToDeath(const sp<hidl_death_recipient>& recipient,
+                             uint64_t /*cookie*/) override;
+
+    // V1_0 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute,
+                (const V1_0::Request& request, const sp<V1_0::IExecutionCallback>& callback),
+                (override));
+
+    // V1_2 methods below.
+    MOCK_METHOD(Return<V1_0::ErrorStatus>, execute_1_2,
+                (const V1_0::Request& request, V1_2::MeasureTiming measure,
+                 const sp<V1_2::IExecutionCallback>& callback),
+                (override));
+    MOCK_METHOD(Return<void>, executeSynchronously,
+                (const V1_0::Request& request, V1_2::MeasureTiming measure,
+                 executeSynchronously_cb cb),
+                (override));
+    MOCK_METHOD(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));
+
+    // V1_3 methods below.
+    MOCK_METHOD(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));
+    MOCK_METHOD(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));
+    MOCK_METHOD(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 cb),
+                (override));
+
+    // Helper methods.
+    void simulateCrash();
+
+  private:
+    sp<hidl_death_recipient> mDeathRecipient;
+};
+
+inline sp<MockPreparedModel> MockPreparedModel::create() {
+    auto mockPreparedModel = sp<MockPreparedModel>::make();
+
+    // Setup default actions for each relevant call.
+    const auto ret = []() -> Return<bool> { return true; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockPreparedModel, linkToDeathRet()).WillByDefault(testing::Invoke(ret));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(testing::AnyNumber());
+
+    return mockPreparedModel;
+}
+
+inline Return<bool> MockPreparedModel::linkToDeath(const sp<hidl_death_recipient>& recipient,
+                                                   uint64_t /*cookie*/) {
+    mDeathRecipient = recipient;
+    return linkToDeathRet();
+}
+
+inline void MockPreparedModel::simulateCrash() {
+    ASSERT_NE(nullptr, mDeathRecipient.get());
+
+    // Currently, the utils::PreparedModel will not use the `cookie` or `who` arguments, so we pass
+    // in 0 and nullptr for these arguments instead. Normally, they are used by the
+    // hidl_death_recipient to determine which object is dead. However, the utils::PreparedModel
+    // code only pairs a single death recipient with a single HIDL interface object, so these
+    // arguments are redundant.
+    mDeathRecipient->serviceDied(0, nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_1_3_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..6dbbd6b
--- /dev/null
+++ b/neuralnetworks/1.3/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,909 @@
+/*
+ * 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 "MockBurstContext.h"
+#include "MockFencedExecutionCallback.h"
+#include "MockPreparedModel.h"
+
+#include <android/hardware/neuralnetworks/1.3/IExecutionCallback.h>
+#include <android/hardware/neuralnetworks/1.3/IFencedExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.3/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace android::hardware::neuralnetworks::V1_3::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+
+const sp<V1_3::IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = V1_2::Timing{.timeOnDevice = std::numeric_limits<uint64_t>::max(),
+                                        .timeInDriver = std::numeric_limits<uint64_t>::max()};
+
+sp<MockPreparedModel> createMockPreparedModel() {
+    const auto mockPreparedModel = MockPreparedModel::create();
+
+    // Ensure that older calls are not used.
+    EXPECT_CALL(*mockPreparedModel, execute(_, _)).Times(0);
+    EXPECT_CALL(*mockPreparedModel, execute_1_2(_, _, _)).Times(0);
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _)).Times(0);
+
+    return mockPreparedModel;
+}
+
+auto makeExecuteSynchronously(V1_3::ErrorStatus status,
+                              const std::vector<V1_2::OutputShape>& outputShapes,
+                              const V1_2::Timing& timing) {
+    return [status, outputShapes, timing](
+                   const V1_3::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+                   const V1_3::OptionalTimePoint& /*deadline*/,
+                   const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+                   const V1_3::IPreparedModel::executeSynchronously_1_3_cb& cb) {
+        cb(status, outputShapes, timing);
+        return hardware::Void();
+    };
+}
+auto makeExecuteAsynchronously(V1_3::ErrorStatus launchStatus, V1_3::ErrorStatus returnStatus,
+                               const std::vector<V1_2::OutputShape>& outputShapes,
+                               const V1_2::Timing& timing) {
+    return [launchStatus, returnStatus, outputShapes, timing](
+                   const V1_3::Request& /*request*/, V1_2::MeasureTiming /*measureTiming*/,
+                   const V1_3::OptionalTimePoint& /*deadline*/,
+                   const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+                   const sp<V1_3::IExecutionCallback>& cb) -> Return<V1_3::ErrorStatus> {
+        cb->notify_1_3(returnStatus, outputShapes, timing);
+        return launchStatus;
+    };
+}
+auto makeExecuteFencedReturn(V1_3::ErrorStatus status, const hardware::hidl_handle& syncFence,
+                             const sp<V1_3::IFencedExecutionCallback>& dispatchCallback) {
+    return [status, syncFence, dispatchCallback](
+                   const V1_3::Request& /*request*/,
+                   const hardware::hidl_vec<hardware::hidl_handle>& /*waitFor*/,
+                   V1_2::MeasureTiming /*measure*/, const V1_3::OptionalTimePoint& /*deadline*/,
+                   const V1_3::OptionalTimeoutDuration& /*loopTimeoutDuration*/,
+                   const V1_3::OptionalTimeoutDuration& /*duration*/,
+                   const V1_3::IPreparedModel::executeFenced_cb& cb) {
+        cb(status, syncFence, dispatchCallback);
+        return hardware::Void();
+    };
+}
+auto makeExecuteFencedCallbackReturn(V1_3::ErrorStatus status, const V1_2::Timing& timingA,
+                                     const V1_2::Timing& timingB) {
+    return [status, timingA,
+            timingB](const V1_3::IFencedExecutionCallback::getExecutionInfo_cb& cb) {
+        cb(status, timingA, timingB);
+        return hardware::Void();
+    };
+}
+auto makeConfigureExecutionBurstReturn(V1_0::ErrorStatus status,
+                                       const sp<MockBurstContext>& burstContext) {
+    return [status, burstContext](
+                   const sp<V1_2::IBurstCallback>& /*callback*/,
+                   const MQDescriptorSync<V1_2::FmqRequestDatum>& /*requestChannel*/,
+                   const MQDescriptorSync<V1_2::FmqResultDatum>& /*resultChannel*/,
+                   V1_2::IPreparedModel::configureExecutionBurst_cb cb) -> hardware::Return<void> {
+        cb(status, burstContext);
+        return hardware::Void();
+    };
+}
+
+std::function<hardware::Status()> makeTransportFailure(status_t status) {
+    return [status] { return hardware::Status::fromStatusT(status); };
+}
+
+const auto makeGeneralTransportFailure = makeTransportFailure(NO_MEMORY);
+const auto makeDeadObjectFailure = makeTransportFailure(DEAD_OBJECT);
+
+}  // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+    // run test
+    const auto result = PreparedModel::create(kInvalidPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto ret = []() -> Return<bool> { return false; };
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet()).Times(1).WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathTransportFailure) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, linkToDeathDeadObject) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModel, linkToDeathRet())
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeSync) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeAsync) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::NONE,
+                                                       V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_3::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_3::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_3::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeFenced) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::NONE, kNoTiming,
+                                                             kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& [syncFence, callback] = result.value();
+    EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+    ASSERT_NE(callback, nullptr);
+
+    // get results from callback
+    const auto callbackResult = callback();
+    ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code << ": "
+                                            << callbackResult.error().message;
+}
+
+TEST(PreparedModelTest, executeFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                             kNoTiming, kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& [syncFence, callback] = result.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr)));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteSynchronously(V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteSynchronously(V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteAsynchronously(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::NONE, {}, kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncLaunchError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                       V1_3::ErrorStatus::GENERAL_FAILURE, {},
+                                                       kNoTiming)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncReturnError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteAsynchronously(
+                    V1_3::ErrorStatus::NONE, V1_3::ErrorStatus::GENERAL_FAILURE, {}, kNoTiming)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteAsyncCrash) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/false).value();
+    const auto ret = [&mockPreparedModel]() -> hardware::Return<V1_3::ErrorStatus> {
+        mockPreparedModel->simulateCrash();
+        return V1_3::ErrorStatus::NONE;
+    };
+    EXPECT_CALL(*mockPreparedModel, execute_1_3(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::NONE,
+                                                                   kNoTiming, kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+        ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+        const auto& [syncFence, callback] = computeResult.value();
+        EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+        ASSERT_NE(callback, nullptr);
+
+        // get results from callback
+        const auto callbackResult = callback();
+        ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+                                                << ": " << callbackResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedCallbackReturn(V1_3::ErrorStatus::GENERAL_FAILURE,
+                                                             kNoTiming, kNoTiming)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeExecuteFencedReturn(V1_3::ErrorStatus::NONE, {}, mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+                                           << computeResult.error().message;
+    const auto& [syncFence, callback] = computeResult.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(
+                    makeExecuteFencedReturn(V1_3::ErrorStatus::GENERAL_FAILURE, {}, nullptr)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+TEST(PreparedModelTest, configureExecutionBurst) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto mockBurstContext = sp<MockBurstContext>::make();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::NONE, mockBurstContext));
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(
+                    makeConfigureExecutionBurstReturn(V1_0::ErrorStatus::GENERAL_FAILURE, nullptr));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+    // setup test
+    const auto mockPreparedModel = createMockPreparedModel();
+    const auto preparedModel =
+            PreparedModel::create(mockPreparedModel, /*executeSynchronously=*/true).value();
+
+    // run test
+    const auto resource = preparedModel->getUnderlyingResource();
+
+    // verify resource
+    const sp<V1_3::IPreparedModel>* maybeMock = std::any_cast<sp<V1_3::IPreparedModel>>(&resource);
+    ASSERT_NE(maybeMock, nullptr);
+    EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+}  // namespace android::hardware::neuralnetworks::V1_3::utils
diff --git a/neuralnetworks/1.3/vts/functional/Android.bp b/neuralnetworks/1.3/vts/functional/Android.bp
index b17d445..f975250 100644
--- a/neuralnetworks/1.3/vts/functional/Android.bp
+++ b/neuralnetworks/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalNeuralNetworksV1_3_utils",
     defaults: ["neuralnetworks_vts_functional_defaults"],
@@ -57,6 +66,7 @@
         "VtsHalNeuralNetworksV1_0_utils",
         "VtsHalNeuralNetworksV1_2_utils",
         "VtsHalNeuralNetworksV1_3_utils",
+        "android.hardware.neuralnetworks-V1-ndk_platform",
         "android.hardware.neuralnetworks@1.0",
         "android.hardware.neuralnetworks@1.1",
         "android.hardware.neuralnetworks@1.2",
diff --git a/neuralnetworks/1.3/vts/functional/AndroidTest.xml b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
index e5acd90..c418aaa 100644
--- a/neuralnetworks/1.3/vts/functional/AndroidTest.xml
+++ b/neuralnetworks/1.3/vts/functional/AndroidTest.xml
@@ -28,5 +28,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNeuralnetworksV1_3TargetTest" />
+        <option name="native-test-timeout" value="20m" />
     </test>
 </configuration>
diff --git a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
index ac18c8f..edffa22 100644
--- a/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/CompilationCachingTests.cpp
@@ -1200,9 +1200,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type);
 }
 
-INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingTest,
-                        testing::Combine(kNamedDeviceChoices, kOperandTypeChoices),
-                        printCompilationCachingTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingTest,
+                         testing::Combine(kNamedDeviceChoices, kOperandTypeChoices),
+                         printCompilationCachingTest);
 
 using CompilationCachingSecurityTestParam = std::tuple<NamedDevice, OperandType, uint32_t>;
 
@@ -1356,9 +1357,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + std::to_string(seed));
 }
 
-INSTANTIATE_TEST_CASE_P(TestCompilationCaching, CompilationCachingSecurityTest,
-                        testing::Combine(kNamedDeviceChoices, kOperandTypeChoices,
-                                         testing::Range(0U, 10U)),
-                        printCompilationCachingSecurityTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingSecurityTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingSecurityTest,
+                         testing::Combine(kNamedDeviceChoices, kOperandTypeChoices,
+                                          testing::Range(0U, 10U)),
+                         printCompilationCachingSecurityTest);
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
index 914a01a..0a95695 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.cpp
@@ -644,7 +644,7 @@
             int n;
             std::tie(n, outputShapes, timing, std::ignore) =
                     controller->compute(request10, testConfig.measureTiming, keys);
-            executionStatus = nn::convertResultCodeToErrorStatus(n);
+            executionStatus = nn::convertToV1_3(nn::convertResultCodeToErrorStatus(n));
 
             break;
         }
diff --git a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
index 4f05c48..834b108 100644
--- a/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
+++ b/neuralnetworks/1.3/vts/functional/GeneratedTestHarness.h
@@ -47,6 +47,7 @@
 std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
 
 #define INSTANTIATE_GENERATED_TEST(TestSuite, filter)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                             \
     INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite,                                    \
                              testing::Combine(testing::ValuesIn(getNamedDevices()),       \
                                               testing::ValuesIn(getNamedModels(filter))), \
diff --git a/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp
index 3c0c885..5facc5e 100644
--- a/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp
+++ b/neuralnetworks/1.3/vts/functional/MemoryDomainTests.cpp
@@ -605,9 +605,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type);
 }
 
-INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainAllocateTest,
-                        testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
-                        printMemoryDomainAllocateTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainAllocateTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainAllocateTest,
+                         testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
+                         printMemoryDomainAllocateTest);
 
 class MemoryDomainCopyTestBase : public MemoryDomainTestBase {
   protected:
@@ -829,9 +830,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type);
 }
 
-INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainCopyTest,
-                        testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
-                        printMemoryDomainCopyTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainCopyTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainCopyTest,
+                         testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices),
+                         printMemoryDomainCopyTest);
 
 using MemoryDomainExecutionTestParam = std::tuple<NamedDevice, TestOperandType, Executor>;
 class MemoryDomainExecutionTest
@@ -1195,9 +1197,10 @@
     return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + executorStr);
 }
 
-INSTANTIATE_TEST_CASE_P(TestMemoryDomain, MemoryDomainExecutionTest,
-                        testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices,
-                                         kExecutorChoices),
-                        printMemoryDomainExecutionTest);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainExecutionTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainExecutionTest,
+                         testing::Combine(kNamedDeviceChoices, kTestOperandTypeChoices,
+                                          kExecutorChoices),
+                         printMemoryDomainExecutionTest);
 
 }  // namespace android::hardware::neuralnetworks::V1_3::vts::functional
diff --git a/neuralnetworks/1.3/vts/functional/TestAssertions.cpp b/neuralnetworks/1.3/vts/functional/TestAssertions.cpp
index a7569e6..1a40721 100644
--- a/neuralnetworks/1.3/vts/functional/TestAssertions.cpp
+++ b/neuralnetworks/1.3/vts/functional/TestAssertions.cpp
@@ -15,10 +15,16 @@
  */
 
 #include <android/hardware/neuralnetworks/1.3/types.h>
+#include "ControlFlow.h"
 #include "TestHarness.h"
 
 namespace android::hardware::neuralnetworks::V1_3 {
 
+static_assert(static_cast<uint64_t>(LoopTimeoutDurationNs::DEFAULT) ==
+              nn::operation_while::kTimeoutNsDefault);
+static_assert(static_cast<uint64_t>(LoopTimeoutDurationNs::MAXIMUM) ==
+              nn::operation_while::kTimeoutNsMaximum);
+
 // Make sure that the HIDL enums are compatible with the values defined in
 // frameworks/ml/nn/tools/test_generator/test_harness/include/TestHarness.h.
 using namespace test_helper;
diff --git a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
index 1ae8b3f..3b441fb 100644
--- a/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
+++ b/neuralnetworks/1.3/vts/functional/ValidateRequest.cpp
@@ -116,7 +116,7 @@
 
         // execute and verify
         const auto [n, outputShapes, timing, fallback] = burst->compute(request10, measure, keys);
-        const ErrorStatus status = nn::convertResultCodeToErrorStatus(n);
+        const ErrorStatus status = nn::convertToV1_3(nn::convertResultCodeToErrorStatus(n));
         EXPECT_EQ(ErrorStatus::INVALID_ARGUMENT, status);
         EXPECT_EQ(outputShapes.size(), 0);
         EXPECT_TRUE(badTiming(timing));
diff --git a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
index a2e5071..7afb05f 100644
--- a/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
+++ b/neuralnetworks/1.3/vts/functional/VtsHalNeuralnetworks.h
@@ -42,6 +42,7 @@
         const testing::TestParamInfo<NeuralnetworksHidlTestParam>& info);
 
 #define INSTANTIATE_DEVICE_TEST(TestSuite)                                                 \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                              \
     INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
                              printNeuralnetworksHidlTest)
 
diff --git a/neuralnetworks/TEST_MAPPING b/neuralnetworks/TEST_MAPPING
index 0cefffa..d296828 100644
--- a/neuralnetworks/TEST_MAPPING
+++ b/neuralnetworks/TEST_MAPPING
@@ -1,6 +1,24 @@
 {
   "presubmit": [
     {
+      "name": "neuralnetworks_utils_hal_common_test"
+    },
+    {
+      "name": "neuralnetworks_utils_hal_1_0_test"
+    },
+    {
+      "name": "neuralnetworks_utils_hal_1_1_test"
+    },
+    {
+      "name": "neuralnetworks_utils_hal_1_2_test"
+    },
+    {
+      "name": "neuralnetworks_utils_hal_1_3_test"
+    },
+    {
+      "name": "neuralnetworks_utils_hal_aidl_test"
+    },
+    {
       "name": "VtsHalNeuralnetworksV1_0TargetTest",
       "options": [
         {
@@ -21,7 +39,9 @@
           "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
         }
       ]
-    },
+    }
+  ],
+  "presubmit-large": [
     {
       "name": "VtsHalNeuralnetworksV1_2TargetTest",
       "options": [
@@ -43,6 +63,17 @@
           "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
         }
       ]
+    },
+    {
+      "name": "VtsHalNeuralnetworksTargetTest",
+      "options": [
+        {
+          // Do not use any sample driver except sample-all in order to reduce
+          // testing time. The other sample drivers (fast-float, quant, etc.)
+          // are subsets of sample-all.
+          "include-filter": "-*sample_float_fast*:*sample_float_slow*:*sample_minimal*:*sample_quant*"
+        }
+      ]
     }
   ]
 }
diff --git a/neuralnetworks/aidl/Android.bp b/neuralnetworks/aidl/Android.bp
new file mode 100644
index 0000000..ebf4654
--- /dev/null
+++ b/neuralnetworks/aidl/Android.bp
@@ -0,0 +1,37 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.neuralnetworks",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/neuralnetworks/*.aidl",
+    ],
+    stability: "vintf",
+    imports: [
+        "android.hardware.common",
+        "android.hardware.graphics.common",
+    ],
+    backend: {
+        java: {
+            enabled: false,
+        },
+        cpp: {
+            enabled: false,
+        },
+        ndk: {
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.neuralnetworks",
+                "test_com.android.neuralnetworks",
+            ],
+            min_sdk_version: "30",
+        },
+    },
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
new file mode 100644
index 0000000..05cec76
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable BufferDesc {
+  int[] dimensions;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
new file mode 100644
index 0000000..10a6b75
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/BufferRole.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable BufferRole {
+  int modelIndex;
+  int ioIndex;
+  float probability;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
new file mode 100644
index 0000000..30877c0
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Capabilities.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Capabilities {
+  android.hardware.neuralnetworks.PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
+  android.hardware.neuralnetworks.PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+  android.hardware.neuralnetworks.OperandPerformance[] operandPerformance;
+  android.hardware.neuralnetworks.PerformanceInfo ifPerformance;
+  android.hardware.neuralnetworks.PerformanceInfo whilePerformance;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
new file mode 100644
index 0000000..db49a38
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DataLocation.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable DataLocation {
+  int poolIndex;
+  long offset;
+  long length;
+  long padding;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
new file mode 100644
index 0000000..7cdd6db
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceBuffer.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable DeviceBuffer {
+  android.hardware.neuralnetworks.IBuffer buffer;
+  int token;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
new file mode 100644
index 0000000..82fe8ae
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/DeviceType.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum DeviceType {
+  OTHER = 1,
+  CPU = 2,
+  GPU = 3,
+  ACCELERATOR = 4,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
new file mode 100644
index 0000000..57d5d6e
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ErrorStatus.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum ErrorStatus {
+  NONE = 0,
+  DEVICE_UNAVAILABLE = 1,
+  GENERAL_FAILURE = 2,
+  OUTPUT_INSUFFICIENT_SIZE = 3,
+  INVALID_ARGUMENT = 4,
+  MISSED_DEADLINE_TRANSIENT = 5,
+  MISSED_DEADLINE_PERSISTENT = 6,
+  RESOURCE_EXHAUSTED_TRANSIENT = 7,
+  RESOURCE_EXHAUSTED_PERSISTENT = 8,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
new file mode 100644
index 0000000..4352d8f
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionPreference.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum ExecutionPreference {
+  LOW_POWER = 0,
+  FAST_SINGLE_ANSWER = 1,
+  SUSTAINED_SPEED = 2,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
new file mode 100644
index 0000000..44e9922
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExecutionResult.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable ExecutionResult {
+  boolean outputSufficientSize;
+  android.hardware.neuralnetworks.OutputShape[] outputShapes;
+  android.hardware.neuralnetworks.Timing timing;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
new file mode 100644
index 0000000..c47028d
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Extension.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Extension {
+  String name;
+  android.hardware.neuralnetworks.ExtensionOperandTypeInformation[] operandTypes;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
new file mode 100644
index 0000000..6c287fd
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable ExtensionNameAndPrefix {
+  String name;
+  char prefix;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
new file mode 100644
index 0000000..a3680aa
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable ExtensionOperandTypeInformation {
+  char type;
+  boolean isTensor;
+  int byteSize;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FencedExecutionResult.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FencedExecutionResult.aidl
new file mode 100644
index 0000000..7952b34
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FencedExecutionResult.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable FencedExecutionResult {
+  android.hardware.neuralnetworks.IFencedExecutionCallback callback;
+  @nullable ParcelFileDescriptor syncFence;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
new file mode 100644
index 0000000..7e61bbb
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum FusedActivationFunc {
+  NONE = 0,
+  RELU = 1,
+  RELU1 = 2,
+  RELU6 = 3,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
new file mode 100644
index 0000000..f10e7e2
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBuffer.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IBuffer {
+  void copyFrom(in android.hardware.neuralnetworks.Memory src, in int[] dimensions);
+  void copyTo(in android.hardware.neuralnetworks.Memory dst);
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
new file mode 100644
index 0000000..eb3d0b0
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IBurst.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IBurst {
+  android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in long[] memoryIdentifierTokens, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
+  void releaseMemoryResource(in long memoryIdentifierToken);
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
new file mode 100644
index 0000000..c9c67f2
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IDevice.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IDevice {
+  android.hardware.neuralnetworks.DeviceBuffer allocate(in android.hardware.neuralnetworks.BufferDesc desc, in android.hardware.neuralnetworks.IPreparedModelParcel[] preparedModels, in android.hardware.neuralnetworks.BufferRole[] inputRoles, in android.hardware.neuralnetworks.BufferRole[] outputRoles);
+  android.hardware.neuralnetworks.Capabilities getCapabilities();
+  android.hardware.neuralnetworks.NumberOfCacheFiles getNumberOfCacheFilesNeeded();
+  android.hardware.neuralnetworks.Extension[] getSupportedExtensions();
+  boolean[] getSupportedOperations(in android.hardware.neuralnetworks.Model model);
+  android.hardware.neuralnetworks.DeviceType getType();
+  String getVersionString();
+  void prepareModel(in android.hardware.neuralnetworks.Model model, in android.hardware.neuralnetworks.ExecutionPreference preference, in android.hardware.neuralnetworks.Priority priority, in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
+  void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache, in ParcelFileDescriptor[] dataCache, in byte[] token, in android.hardware.neuralnetworks.IPreparedModelCallback callback);
+  const int BYTE_SIZE_OF_CACHE_TOKEN = 32;
+  const int MAX_NUMBER_OF_CACHE_FILES = 32;
+  const int EXTENSION_TYPE_HIGH_BITS_PREFIX = 15;
+  const int EXTENSION_TYPE_LOW_BITS_TYPE = 16;
+  const int OPERAND_TYPE_BASE_MAX = 65535;
+  const int OPERATION_TYPE_BASE_MAX = 65535;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
new file mode 100644
index 0000000..0bfb80a
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IFencedExecutionCallback {
+  android.hardware.neuralnetworks.ErrorStatus getExecutionInfo(out android.hardware.neuralnetworks.Timing timingLaunched, out android.hardware.neuralnetworks.Timing timingFenced);
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
new file mode 100644
index 0000000..fccb5dc
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IPreparedModel {
+  android.hardware.neuralnetworks.ExecutionResult executeSynchronously(in android.hardware.neuralnetworks.Request request, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
+  android.hardware.neuralnetworks.FencedExecutionResult executeFenced(in android.hardware.neuralnetworks.Request request, in ParcelFileDescriptor[] waitFor, in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs, in long durationNs);
+  android.hardware.neuralnetworks.IBurst configureExecutionBurst();
+  const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
+  const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
new file mode 100644
index 0000000..e0c763b
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+interface IPreparedModelCallback {
+  void notify(in android.hardware.neuralnetworks.ErrorStatus status, in android.hardware.neuralnetworks.IPreparedModel preparedModel);
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
new file mode 100644
index 0000000..dbedf12
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable IPreparedModelParcel {
+  android.hardware.neuralnetworks.IPreparedModel preparedModel;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
new file mode 100644
index 0000000..37fa102
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Memory.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+union Memory {
+  android.hardware.common.Ashmem ashmem;
+  android.hardware.common.MappableFile mappableFile;
+  android.hardware.graphics.common.HardwareBuffer hardwareBuffer;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
new file mode 100644
index 0000000..30d8dda
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Model.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Model {
+  android.hardware.neuralnetworks.Subgraph main;
+  android.hardware.neuralnetworks.Subgraph[] referenced;
+  byte[] operandValues;
+  android.hardware.neuralnetworks.Memory[] pools;
+  boolean relaxComputationFloat32toFloat16;
+  android.hardware.neuralnetworks.ExtensionNameAndPrefix[] extensionNameToPrefix;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
new file mode 100644
index 0000000..9314760
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable NumberOfCacheFiles {
+  int numModelCache;
+  int numDataCache;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
new file mode 100644
index 0000000..1d9bdd8
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operand.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Operand {
+  android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
+  int[] dimensions;
+  float scale;
+  int zeroPoint;
+  android.hardware.neuralnetworks.OperandLifeTime lifetime = android.hardware.neuralnetworks.OperandLifeTime.TEMPORARY_VARIABLE;
+  android.hardware.neuralnetworks.DataLocation location;
+  @nullable android.hardware.neuralnetworks.OperandExtraParams extraParams;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
new file mode 100644
index 0000000..14792cf
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandExtraParams.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+union OperandExtraParams {
+  android.hardware.neuralnetworks.SymmPerChannelQuantParams channelQuant;
+  byte[] extension;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
new file mode 100644
index 0000000..40adfb1
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandLifeTime.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum OperandLifeTime {
+  TEMPORARY_VARIABLE = 0,
+  SUBGRAPH_INPUT = 1,
+  SUBGRAPH_OUTPUT = 2,
+  CONSTANT_COPY = 3,
+  CONSTANT_POOL = 4,
+  NO_VALUE = 5,
+  SUBGRAPH = 6,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
new file mode 100644
index 0000000..ebb361b
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable OperandPerformance {
+  android.hardware.neuralnetworks.OperandType type = android.hardware.neuralnetworks.OperandType.FLOAT32;
+  android.hardware.neuralnetworks.PerformanceInfo info;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
new file mode 100644
index 0000000..9f2c759
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperandType.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum OperandType {
+  FLOAT32 = 0,
+  INT32 = 1,
+  UINT32 = 2,
+  TENSOR_FLOAT32 = 3,
+  TENSOR_INT32 = 4,
+  TENSOR_QUANT8_ASYMM = 5,
+  BOOL = 6,
+  TENSOR_QUANT16_SYMM = 7,
+  TENSOR_FLOAT16 = 8,
+  TENSOR_BOOL8 = 9,
+  FLOAT16 = 10,
+  TENSOR_QUANT8_SYMM_PER_CHANNEL = 11,
+  TENSOR_QUANT16_ASYMM = 12,
+  TENSOR_QUANT8_SYMM = 13,
+  TENSOR_QUANT8_ASYMM_SIGNED = 14,
+  SUBGRAPH = 15,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
new file mode 100644
index 0000000..a4a3fbe
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Operation.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Operation {
+  android.hardware.neuralnetworks.OperationType type = android.hardware.neuralnetworks.OperationType.ADD;
+  int[] inputs;
+  int[] outputs;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
new file mode 100644
index 0000000..de3b438
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OperationType.aidl
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum OperationType {
+  ADD = 0,
+  AVERAGE_POOL_2D = 1,
+  CONCATENATION = 2,
+  CONV_2D = 3,
+  DEPTHWISE_CONV_2D = 4,
+  DEPTH_TO_SPACE = 5,
+  DEQUANTIZE = 6,
+  EMBEDDING_LOOKUP = 7,
+  FLOOR = 8,
+  FULLY_CONNECTED = 9,
+  HASHTABLE_LOOKUP = 10,
+  L2_NORMALIZATION = 11,
+  L2_POOL_2D = 12,
+  LOCAL_RESPONSE_NORMALIZATION = 13,
+  LOGISTIC = 14,
+  LSH_PROJECTION = 15,
+  LSTM = 16,
+  MAX_POOL_2D = 17,
+  MUL = 18,
+  RELU = 19,
+  RELU1 = 20,
+  RELU6 = 21,
+  RESHAPE = 22,
+  RESIZE_BILINEAR = 23,
+  RNN = 24,
+  SOFTMAX = 25,
+  SPACE_TO_DEPTH = 26,
+  SVDF = 27,
+  TANH = 28,
+  BATCH_TO_SPACE_ND = 29,
+  DIV = 30,
+  MEAN = 31,
+  PAD = 32,
+  SPACE_TO_BATCH_ND = 33,
+  SQUEEZE = 34,
+  STRIDED_SLICE = 35,
+  SUB = 36,
+  TRANSPOSE = 37,
+  ABS = 38,
+  ARGMAX = 39,
+  ARGMIN = 40,
+  AXIS_ALIGNED_BBOX_TRANSFORM = 41,
+  BIDIRECTIONAL_SEQUENCE_LSTM = 42,
+  BIDIRECTIONAL_SEQUENCE_RNN = 43,
+  BOX_WITH_NMS_LIMIT = 44,
+  CAST = 45,
+  CHANNEL_SHUFFLE = 46,
+  DETECTION_POSTPROCESSING = 47,
+  EQUAL = 48,
+  EXP = 49,
+  EXPAND_DIMS = 50,
+  GATHER = 51,
+  GENERATE_PROPOSALS = 52,
+  GREATER = 53,
+  GREATER_EQUAL = 54,
+  GROUPED_CONV_2D = 55,
+  HEATMAP_MAX_KEYPOINT = 56,
+  INSTANCE_NORMALIZATION = 57,
+  LESS = 58,
+  LESS_EQUAL = 59,
+  LOG = 60,
+  LOGICAL_AND = 61,
+  LOGICAL_NOT = 62,
+  LOGICAL_OR = 63,
+  LOG_SOFTMAX = 64,
+  MAXIMUM = 65,
+  MINIMUM = 66,
+  NEG = 67,
+  NOT_EQUAL = 68,
+  PAD_V2 = 69,
+  POW = 70,
+  PRELU = 71,
+  QUANTIZE = 72,
+  QUANTIZED_16BIT_LSTM = 73,
+  RANDOM_MULTINOMIAL = 74,
+  REDUCE_ALL = 75,
+  REDUCE_ANY = 76,
+  REDUCE_MAX = 77,
+  REDUCE_MIN = 78,
+  REDUCE_PROD = 79,
+  REDUCE_SUM = 80,
+  ROI_ALIGN = 81,
+  ROI_POOLING = 82,
+  RSQRT = 83,
+  SELECT = 84,
+  SIN = 85,
+  SLICE = 86,
+  SPLIT = 87,
+  SQRT = 88,
+  TILE = 89,
+  TOPK_V2 = 90,
+  TRANSPOSE_CONV_2D = 91,
+  UNIDIRECTIONAL_SEQUENCE_LSTM = 92,
+  UNIDIRECTIONAL_SEQUENCE_RNN = 93,
+  RESIZE_NEAREST_NEIGHBOR = 94,
+  QUANTIZED_LSTM = 95,
+  IF = 96,
+  WHILE = 97,
+  ELU = 98,
+  HARD_SWISH = 99,
+  FILL = 100,
+  RANK = 101,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
new file mode 100644
index 0000000..f733505
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/OutputShape.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable OutputShape {
+  int[] dimensions;
+  boolean isSufficient;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
new file mode 100644
index 0000000..04910f5
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/PerformanceInfo.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable PerformanceInfo {
+  float execTime;
+  float powerUsage;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
new file mode 100644
index 0000000..8f35709
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Priority.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@Backing(type="int") @VintfStability
+enum Priority {
+  LOW = 0,
+  MEDIUM = 1,
+  HIGH = 2,
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
new file mode 100644
index 0000000..39ec7a9
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Request.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Request {
+  android.hardware.neuralnetworks.RequestArgument[] inputs;
+  android.hardware.neuralnetworks.RequestArgument[] outputs;
+  android.hardware.neuralnetworks.RequestMemoryPool[] pools;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
new file mode 100644
index 0000000..e3541c0
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestArgument.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable RequestArgument {
+  boolean hasNoValue;
+  android.hardware.neuralnetworks.DataLocation location;
+  int[] dimensions;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
new file mode 100644
index 0000000..312f581
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+union RequestMemoryPool {
+  android.hardware.neuralnetworks.Memory pool;
+  int token;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
new file mode 100644
index 0000000..b7d4451
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Subgraph.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Subgraph {
+  android.hardware.neuralnetworks.Operand[] operands;
+  android.hardware.neuralnetworks.Operation[] operations;
+  int[] inputIndexes;
+  int[] outputIndexes;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
new file mode 100644
index 0000000..02d68f9
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable SymmPerChannelQuantParams {
+  float[] scales;
+  int channelDim;
+}
diff --git a/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
new file mode 100644
index 0000000..bcc83cf
--- /dev/null
+++ b/neuralnetworks/aidl/aidl_api/android.hardware.neuralnetworks/current/android/hardware/neuralnetworks/Timing.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.neuralnetworks;
+@VintfStability
+parcelable Timing {
+  long timeOnDeviceNs;
+  long timeInDriverNs;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
new file mode 100644
index 0000000..bec7e86
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferDesc.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * A buffer descriptor. Describes the properties of a buffer.
+ */
+@VintfStability
+parcelable BufferDesc {
+    /**
+     * Dimensions of the buffer. May have unknown dimensions or rank. A buffer with some number of
+     * unspecified dimensions is represented by setting each unspecified dimension to 0. A buffer
+     * with unspecified rank is represented by providing an empty dimensions vector.
+     */
+    int[] dimensions;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
new file mode 100644
index 0000000..c444851
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/BufferRole.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Describes a role of an input or output to a prepared model.
+ */
+@VintfStability
+parcelable BufferRole {
+    /**
+     * The index of the IPreparedModel within the "preparedModel" argument passed in
+     * IDevice::allocate.
+     */
+    int modelIndex;
+    /**
+     * The index of the input or output operand.
+     */
+    int ioIndex;
+    /**
+     * A floating-point value within the range (0.0, 1.0]. Describes how likely the buffer is to be
+     * used in the specified role. This is provided as a hint to optimize the case when multiple
+     * roles prefer different buffer locations or data layouts.
+     */
+    float probability;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl
new file mode 100644
index 0000000..3802f1f
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Capabilities.aidl
@@ -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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.OperandPerformance;
+import android.hardware.neuralnetworks.PerformanceInfo;
+
+/**
+ * The capabilities of a driver.
+ *
+ * This represents performance of non-extension operations.
+ *
+ * Performance of an operation other than {@link OperationType::IF} and {@link OperationType::WHILE}
+ * comes from the type of its first operand.
+ */
+@VintfStability
+parcelable Capabilities {
+    /**
+     * Driver performance when operating on float32 data but performing calculations with range
+     * and/or precision as low as that of the IEEE 754 16-bit floating-point format.
+     */
+    PerformanceInfo relaxedFloat32toFloat16PerformanceScalar;
+    PerformanceInfo relaxedFloat32toFloat16PerformanceTensor;
+    /**
+     * Performance by operand type. Must be sorted by OperandType.
+     *
+     * If a particular {@link OperandType} is not present in operandPerformance, its performance is
+     * treated as { .execTime = FLT_MAX, .powerUsage = FLT_MAX }.
+     *
+     * Performance does not apply to {@link OperandType::SUBGRAPH}, and a driver must not report
+     * operand performance for {@link OperandType::SUBGRAPH}.
+     */
+    OperandPerformance[] operandPerformance;
+    /**
+     * Performance of an {@link OperationType::IF} operation is the sum of
+     * {@link Capabilities::ifPerformance} and the mean of performance for the two branch subgraphs,
+     * where performance for a subgraph is the sum of the performance of all operations within the
+     * subgraph.
+     */
+    PerformanceInfo ifPerformance;
+    /**
+     * Performance of a {@link OperationType::WHILE} operation is the sum of
+     * {@link Capabilities::whilePerformance}, performance for the condition subgraph and
+     * performance for the body subgraph, where performance for a subgraph is the sum of the
+     * performance of all operations within the subgraph.
+     */
+    PerformanceInfo whilePerformance;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
new file mode 100644
index 0000000..f656360
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DataLocation.aidl
@@ -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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Describes the location of a data object.
+ *
+ * If the data object is an omitted operand, all of the fields must be 0. If the poolIndex refers to
+ * a driver-managed buffer allocated from IDevice::allocate, or an AHardwareBuffer of a format other
+ * than AHARDWAREBUFFER_FORMAT_BLOB, the offset, length, and padding must be set to 0 indicating
+ * the entire pool is used.
+ *
+ * Otherwise, the offset, length, and padding specify a sub-region of a memory pool. The sum of
+ * offset, length, and padding must not exceed the total size of the specified memory pool. If the
+ * data object is a scalar operand or a tensor operand with fully specified dimensions, the value of
+ * length must be equal to the raw size of the operand (i.e. the size of an element multiplied
+ * by the number of elements). When used in Operand, the value of padding must be 0. When used in
+ * RequestArgument, the value of padding specifies the extra bytes at the end of the memory region
+ * that may be used by the device to access memory in chunks, for efficiency. If the data object is
+ * a Request output whose dimensions are not fully specified, the value of length specifies the
+ * total size of the writable region of the output data, and padding specifies the extra bytes at
+ * the end of the memory region that may be used by the device to access memory in chunks, for
+ * efficiency, but must not be used to hold any output data.
+ *
+ * When used in RequestArgument, clients should prefer to align and pad the sub-region to
+ * 64 bytes when possible; this may allow the device to access the sub-region more efficiently.
+ * The sub-region is aligned to 64 bytes if the value of offset is a multiple of 64.
+ * The sub-region is padded to 64 bytes if the sum of length and padding is a multiple of 64.
+ */
+@VintfStability
+parcelable DataLocation {
+    /**
+     * The index of the memory pool where this location is found.
+     */
+    int poolIndex;
+    /**
+     * Offset in bytes from the start of the pool.
+     */
+    long offset;
+    /**
+     * The length of the data in bytes.
+     */
+    long length;
+    /**
+     * The end padding of the specified memory region in bytes.
+     */
+    long padding;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl
new file mode 100644
index 0000000..07930a6
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceBuffer.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.IBuffer;
+
+/**
+ * A type that is used to represent a driver allocated buffer and token that corresponds to it.
+ */
+@VintfStability
+parcelable DeviceBuffer {
+    /**
+     * An IBuffer object used to interact with the device allocated buffer.
+     */
+    IBuffer buffer;
+    /**
+     * A positive token identifying the allocated buffer. The token is provided when referencing the
+     * buffer as one of the memory pools in the request of an execution. The token must not collide
+     * with the tokens of other IBuffer objects that are currently alive in the same driver service.
+     */
+    int token;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl
new file mode 100644
index 0000000..815be64
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/DeviceType.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Device types.
+ *
+ * The type of NNAPI device.
+ */
+@VintfStability
+@Backing(type="int")
+enum DeviceType {
+    /**
+     * The device does not fall into any category below.
+     */
+    OTHER = 1,
+    /**
+     * The device runs NNAPI models on single or multi-core CPU.
+     */
+    CPU = 2,
+    /**
+     * The device can run NNAPI models and also accelerate graphics APIs such as OpenGL ES and
+     * Vulkan.
+     */
+    GPU = 3,
+    /**
+     * Dedicated accelerator for Machine Learning workloads.
+     */
+    ACCELERATOR = 4,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl
new file mode 100644
index 0000000..c2752d9
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ErrorStatus.aidl
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Calls to neural networks AIDL interfaces may return a ServiceSpecificException with the following
+ * error codes.
+ */
+@VintfStability
+@Backing(type="int")
+enum ErrorStatus {
+    NONE,
+    DEVICE_UNAVAILABLE,
+    GENERAL_FAILURE,
+    OUTPUT_INSUFFICIENT_SIZE,
+    INVALID_ARGUMENT,
+    /**
+     * Failure because a deadline could not be met for a task, but future deadlines may still be met
+     * for the same task after a short delay.
+     */
+    MISSED_DEADLINE_TRANSIENT,
+    /**
+     * Failure because a deadline could not be met for a task, and future deadlines will likely also
+     * not be met for the same task even after a short delay.
+     */
+    MISSED_DEADLINE_PERSISTENT,
+    /**
+     * Failure because of a resource limitation within the driver, but future calls for the same
+     * task may still succeed after a short delay.
+     */
+    RESOURCE_EXHAUSTED_TRANSIENT,
+    /**
+     * Failure because of a resource limitation within the driver, and future calls for the same
+     * task will likely also fail even after a short delay.
+     */
+    RESOURCE_EXHAUSTED_PERSISTENT,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl
new file mode 100644
index 0000000..a3e3ce3
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionPreference.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Execution preferences.
+ */
+@VintfStability
+@Backing(type="int")
+enum ExecutionPreference {
+    /**
+     * Prefer executing in a way that minimizes battery drain. This is desirable for compilations
+     * that will be executed often.
+     */
+    LOW_POWER,
+    /**
+     * Prefer returning a single answer as fast as possible, even if this causes more power
+     * consumption.
+     */
+    FAST_SINGLE_ANSWER,
+    /**
+     * Prefer maximizing the throughput of successive frames, for example when processing successive
+     * frames coming from the camera.
+     */
+    SUSTAINED_SPEED,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl
new file mode 100644
index 0000000..1f88207
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExecutionResult.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.OutputShape;
+import android.hardware.neuralnetworks.Timing;
+
+/**
+ * A result from running a synchronous execution of a prepared model.
+ */
+@VintfStability
+parcelable ExecutionResult {
+    /**
+     * A value of "true" indicates that the execution was successful. A value of "false" indicates
+     * the execution failed because at least one output operand buffer was not large enough to store
+     * the corresponding output.
+     */
+    boolean outputSufficientSize;
+    /**
+     * A list of shape information of model output operands. The index in "outputShapes" corresponds
+     * to the index of the output operand in the Request outputs vector.
+     */
+    OutputShape[] outputShapes;
+    /**
+     * Duration of execution. Unless measure is true and the execution is successful, all times must
+     * be reported as -1. A driver may choose to report any time as -1, indicating that measurement
+     * is not available.
+     */
+    Timing timing;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl
new file mode 100644
index 0000000..20109bd
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Extension.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ExtensionOperandTypeInformation;
+
+/**
+ * Information about an extension.
+ */
+@VintfStability
+parcelable Extension {
+    /**
+     * The extension name.
+     *
+     * The name must consist of lowercase latin letters, numbers, periods, and underscore signs. The
+     * name must contain at least one period.
+     *
+     * The name must start with the reverse domain name of the vendor.
+     *
+     * Example: com.google.test_extension
+     */
+    String name;
+    /**
+     * Information about operand types defined by the extension.
+     */
+    ExtensionOperandTypeInformation[] operandTypes;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
new file mode 100644
index 0000000..29be93f
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * The mapping between extension names and prefixes of operand and operation type values.
+ *
+ * An operand or operation whose numeric type value is above {@link IDevice::OPERAND_TYPE_BASE_MAX}
+ * or {@link IDevice::OPERATION_TYPE_BASE_MAX} respectively should be interpreted as an extension
+ * operand/operation. The low {@link IDevice::EXTENSION_TYPE_LOW_BITS_TYPE} bits of the value
+ * correspond to the type ID within the extension and the high
+ * {@link IDevice::EXTENSION_TYPE_HIGH_BITS_PREFIX} bits encode the "prefix", which maps uniquely to
+ * the extension name. The sign bit is always 0.
+ *
+ * For example, if a model contains an operation whose value is 0x7AAABBBB and extensionNameToPrefix
+ * contains an entry with prefix=0x7AAA and name="vendor.test.test_extension", then the operation
+ * should be interpreted as the operation 0xBBBB of the extension named vendor.test.test_extension.
+ *
+ * This is a one-to-one correspondence. That is, there must be at most one prefix corresponding to
+ * each extension name and at most one extension name corresponding to each prefix.
+ */
+@VintfStability
+parcelable ExtensionNameAndPrefix {
+    /**
+     * The extension name.
+     *
+     * See {@link Extension::name} for the format specification.
+     */
+    String name;
+    /**
+     * The extension prefix. Only the lowest 15 bits are used, so the value must be less than 32768.
+     */
+    char prefix;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
new file mode 100644
index 0000000..b8e3449
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Information about an extension operand type.
+ */
+@VintfStability
+parcelable ExtensionOperandTypeInformation {
+    /**
+     * The extension operand type.
+     */
+    char type;
+    /**
+     * Indicates whether the extension operand type represents a tensor or a scalar.
+     */
+    boolean isTensor;
+    /**
+     * The byte size of the operand (if scalar) or of a single element (if tensor).
+     */
+    int byteSize;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/FencedExecutionResult.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/FencedExecutionResult.aidl
new file mode 100644
index 0000000..ba3be31
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/FencedExecutionResult.aidl
@@ -0,0 +1,37 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.IFencedExecutionCallback;
+
+/**
+ * A result from running an asynchronous execution of a prepared model.
+ */
+@VintfStability
+parcelable FencedExecutionResult {
+    /**
+     * IFencedExecutionCallback can be used to query information like duration and error
+     * status when the execution is completed.
+     */
+    IFencedExecutionCallback callback;
+    /**
+     * The sync fence that will be signaled when the task is completed. The
+     * sync fence will be set to error if a critical error, e.g. hardware
+     * failure or kernel panic, occurs when doing execution.
+     */
+    @nullable ParcelFileDescriptor syncFence;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
new file mode 100644
index 0000000..df015ca
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/FusedActivationFunc.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Fused activation function types.
+ */
+@VintfStability
+@Backing(type="int")
+enum FusedActivationFunc {
+    /**
+     * No activation.
+     */
+    NONE,
+    /**
+     * ReLU(x) = max(0, x)
+     */
+    RELU,
+    /**
+     * ReLU1(x) = min(1, max(-1, x))
+     */
+    RELU1,
+    /**
+     * ReLU6(x) = min(6, max(0, x))
+     */
+    RELU6,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl
new file mode 100644
index 0000000..2915a22
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBuffer.aidl
@@ -0,0 +1,57 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.Memory;
+
+/**
+ * This interface represents a device memory buffer.
+ */
+@VintfStability
+interface IBuffer {
+    /**
+     * Sets the content of this buffer from a shared memory region.
+     *
+     * @param src The source shared memory region.
+     * @param dimensions Updated dimensional information. If the dimensions of the IBuffer object
+     *                   are not fully specified, then the dimensions must be fully specified here.
+     *                   If the dimensions of the IBuffer object are fully specified, then the
+     *                   dimensions may be empty here. If dimensions.size() > 0, then all dimensions
+     *                   must be specified here, and any dimension that was specified in the IBuffer
+     *                   object must have the same value here.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if provided memory is invalid, or if the dimensions is invalid
+     */
+    void copyFrom(in Memory src, in int[] dimensions);
+
+    /**
+     * Retrieves the content of this buffer to a shared memory region.
+     *
+     * The IBuffer object must have been initialized before the call to IBuffer::copyTo. For more
+     * information on the state of the IBuffer object, refer to IDevice::allocate.
+     *
+     * @param dst The destination shared memory region.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if the IBuffer object is uninitialized, or there is an unspecified
+     *       error
+     *     - INVALID_ARGUMENT if provided memory is invalid
+     */
+    void copyTo(in Memory dst);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
new file mode 100644
index 0000000..decdc48
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IBurst.aidl
@@ -0,0 +1,120 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.ExecutionResult;
+import android.hardware.neuralnetworks.Request;
+
+/**
+ * IBurst represents a burst execution object.
+ *
+ * Burst executions are a sequence of executions of the same prepared model that occur in rapid
+ * succession, such as frames of a camera capture or successive audio samples. A burst object is
+ * used to control a set of burst executions, and to preserve resources between executions, enabling
+ * executions to have lower overhead. Burst objects enable some optimizations:
+ * (1) A burst object is created before a sequence of executions, and freed when the sequence has
+ *     ended. Because of this, the lifetime of the burst object hints to a driver how long it should
+ *     remain in a high performance state.
+ * (2) A burst object can preserve resources between executions. For example, a driver can map a
+ *     memory object on the first execution and cache the mapping in the burst object for reuse in
+ *     subsequent executions. Any cached resource can be released when the burst object is destroyed
+ *     or when the NNAPI runtime notifies the burst object that the resource is no longer required.
+ * (3) A burst object may be used for at most one execution at a time. This enables any transient
+ *     execution resources such as intermediate tensors to be allocated once when the burst object
+ *     is created and freed when the burst object is destroyed.
+ */
+@VintfStability
+interface IBurst {
+    /**
+     * Performs a synchronous execution on a burst object.
+     *
+     * The execution is performed synchronously with respect to the caller. executeSynchronously
+     * must verify the inputs to the function are correct, and the usages of memory pools allocated
+     * by IDevice::allocate are valid. If there is an error, executeSynchronously must immediately
+     * return a service specific exception with the appropriate ErrorStatus value. If the inputs to
+     * the function are valid and there is no error, executeSynchronously must perform the
+     * execution, and must not return until the execution is complete.
+     *
+     * The caller must not change the content of any data object referenced by 'request' (described
+     * by the {@link DataLocation} of a {@link RequestArgument}) until executeSynchronously returns.
+     * executeSynchronously must not change the content of any of the data objects corresponding to
+     * 'request' inputs.
+     *
+     * If the burst object was configured from a prepared model wherein all tensor operands have
+     * fully specified dimensions, and the inputs to the function are valid, and at execution time
+     * every operation's input operands have legal values, then the execution should complete
+     * successfully: there must be no failure unless the device itself is in a bad state.
+     *
+     * executeSynchronously may be called with an optional deadline. If the execution is not able to
+     * be completed before the provided deadline, the execution may be aborted, and either
+     * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
+     * sent the same way as other errors, described above.
+     *
+     * Only a single execution on a given burst object may be active at any time.
+     *
+     * @param request The input and output information on which the prepared model is to be
+     *                executed.
+     * @param memoryIdentifierTokens A list of tokens where each token is a non-negative number
+     *                               that uniquely identifies a memory object. Each memory
+     *                               identifier token corresponds to an element of request.pools. A
+     *                               value of -1 indicates no identity.
+     * @param measure Specifies whether or not to measure duration of the execution. The duration
+     *                runs from the time the driver sees the call to the executeSynchronously
+     *                function to the time the driver returns from the function.
+     * @param deadlineNs The time by which the execution is expected to complete. The time is
+     *                   measured in nanoseconds since epoch of the steady clock (as from
+     *                   std::chrono::steady_clock). If the execution cannot be finished by the
+     *                   deadline, the execution may be aborted. Passing -1 means the deadline is
+     *                   omitted. Other negative values are invalid.
+     * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+     *                              executing a {@link OperationType::WHILE} operation. If a loop
+     *                              condition model does not output false within this duration, the
+     *                              execution must be aborted. If -1 is provided, the maximum amount
+     *                              of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+     *                              negative values are invalid. When provided, the duration must
+     *                              not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+     * @return ExecutionResult parcelable, containing the status of the execution, output shapes and
+     *     timing information.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid
+     *     - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
+     *       deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    ExecutionResult executeSynchronously(in Request request, in long[] memoryIdentifierTokens,
+            in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs);
+
+    /**
+     * releaseMemoryResource is used by the client to signal to the service that a memory buffer
+     * corresponding to a slot number is no longer needed by the client, and any cached resources
+     * associated with that memory object may be released.
+     *
+     * The identifier tokens are unique to the burst object.
+     *
+     * @param memoryIdentifierToken Value uniquely identifying a memory object that is no longer
+     *                              used.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid
+     */
+    void releaseMemoryResource(in long memoryIdentifierToken);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
new file mode 100644
index 0000000..72e2623
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IDevice.aidl
@@ -0,0 +1,432 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.BufferDesc;
+import android.hardware.neuralnetworks.BufferRole;
+import android.hardware.neuralnetworks.Capabilities;
+import android.hardware.neuralnetworks.DeviceBuffer;
+import android.hardware.neuralnetworks.DeviceType;
+import android.hardware.neuralnetworks.ExecutionPreference;
+import android.hardware.neuralnetworks.Extension;
+import android.hardware.neuralnetworks.IPreparedModel;
+import android.hardware.neuralnetworks.IPreparedModelCallback;
+import android.hardware.neuralnetworks.IPreparedModelParcel;
+import android.hardware.neuralnetworks.Model;
+import android.hardware.neuralnetworks.NumberOfCacheFiles;
+import android.hardware.neuralnetworks.Priority;
+
+/**
+ * This interface represents a device driver.
+ */
+@VintfStability
+interface IDevice {
+    /**
+     * The byte size of the cache token.
+     */
+    const int BYTE_SIZE_OF_CACHE_TOKEN = 32;
+    /**
+     * The maximum number of files for each type of cache in compilation caching.
+     */
+    const int MAX_NUMBER_OF_CACHE_FILES = 32;
+
+    /**
+     * Numeric values of extension operand and operation types have the following structure:
+     * - The sign bit is always 0.
+     * - 15 high bits represent the "prefix", which corresponds uniquely to the extension name.
+     * - 16 low bits represent the type ID within the extension.
+     */
+    const int EXTENSION_TYPE_HIGH_BITS_PREFIX = 15;
+    const int EXTENSION_TYPE_LOW_BITS_TYPE = 16;
+    /**
+     * OperandType with any value above {@link IDevice::OPERAND_TYPE_BASE_MAX} must be interpreted
+     * as an extension type according to {@link Model::extensionNameToPrefix}.
+     */
+    const int OPERAND_TYPE_BASE_MAX = 0xFFFF;
+    /**
+     * OperationType with any value above {@link IDevice::OPERATION_TYPE_BASE_MAX} must be
+     * interpreted as an extension type according to {@link Model::extensionNameToPrefix}.
+     */
+    const int OPERATION_TYPE_BASE_MAX = 0xFFFF;
+
+    /**
+     * Allocates a driver-managed buffer with the properties specified by the buffer descriptor as
+     * well as the input and output roles.
+     *
+     * The allocate function must verify its inputs are correct. If there is an error, or if a
+     * certain role or property is not supported by the driver, the allocate function must return a
+     * service specific exception with an appropriate ErrorStatus. If the allocation is successful,
+     * this method must return a DeviceBuffer object with the produced IBuffer and a positive token
+     * identifying the allocated buffer. A successful allocation must accommodate all of the
+     * specified roles and buffer properties.
+     *
+     * The buffer is allocated to an uninitialized state. An uninitialized buffer may only be used
+     * in ways that are specified by outputRoles. A buffer is initialized after it is used as an
+     * output in a successful execution, or after a successful invocation of IBuffer::copyFrom on
+     * the buffer. An initialized buffer may be used according to all roles specified in inputRoles
+     * and outputRoles. A buffer will return to the uninitialized state if it is used as an output
+     * in a failed execution, or after a failed invocation of IBuffer::copyFrom on the buffer.
+     *
+     * The dimensions of the buffer can be deduced from the buffer descriptor as well as the
+     * dimensions of the corresponding model operands of the input and output roles. The dimensions
+     * or rank of the buffer may be unknown at this stage. As such, some driver services may only
+     * create a placeholder and defer the actual allocation until execution time. Note that the same
+     * buffer may be used for different shapes of outputs on different executions. When the buffer
+     * is used as an input, the input shape must be the same as the output shape from the last
+     * execution using this buffer as an output.
+     *
+     * The driver must apply proper validatation upon every usage of the buffer, and must fail the
+     * execution immediately if the usage is illegal.
+     *
+     * @param desc A buffer descriptor specifying the properties of the buffer to allocate.
+     * @param preparedModels A vector of IPreparedModel objects. Must only contain IPreparedModel
+     *                       objects from the same IDevice as this method is being invoked on.
+     * @param inputRoles A vector of roles with each specifying an input to a prepared model.
+     * @param outputRoles A vector of roles with each specifying an output to a prepared model. Each
+     *                    role specified in inputRoles and outputRoles must be unique. The
+     *                    corresponding model operands of the roles must have the same OperandType,
+     *                    scale, zero point, and ExtraParams. The dimensions of the operands and the
+     *                    dimensions specified in the buffer descriptor must be compatible with each
+     *                    other. Two dimensions are incompatible if there is at least one axis that
+     *                    is fully specified in both but has different values.
+     * @return DeviceBuffer object containing the allocated IBuffer object and a positive token that
+     *     can be used to reference the buffer as one of the memory pools.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if a certain buffer property or a certain role is not supported,
+     *       or if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    DeviceBuffer allocate(in BufferDesc desc, in IPreparedModelParcel[] preparedModels,
+            in BufferRole[] inputRoles, in BufferRole[] outputRoles);
+
+    /**
+     * Gets the capabilities of a driver.
+     *
+     * @return Capabilities of the driver.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     */
+    Capabilities getCapabilities();
+
+    /**
+     * Gets the caching requirements of the driver implementation.
+     *
+     * There are two types of cache file descriptors provided to the driver: model cache and data
+     * cache.
+     *
+     * The data cache is for caching constant data, possibly including preprocessed and transformed
+     * tensor buffers. Any modification to the data cache should have no worse effect than
+     * generating bad output values at execution time.
+     *
+     * The model cache is for caching security-sensitive data such as compiled executable machine
+     * code in the device's native binary format. A modification to the model cache may affect the
+     * driver's execution behavior, and a malicious client could make use of this to execute beyond
+     * the granted permission. Thus, the driver must always check whether the model cache is
+     * corrupted before preparing the model from cache.
+     *
+     * getNumberOfCacheFilesNeeded returns how many of each type of cache files the driver
+     * implementation needs to cache a single prepared model. Returning 0 for both types indicates
+     * compilation caching is not supported by this driver. The driver may still choose not to cache
+     * certain compiled models even if it reports that caching is supported.
+     *
+     * If the device reports that caching is not supported, the user may avoid calling
+     * IDevice::prepareModelFromCache or providing cache file descriptors to
+     * IDevice::prepareModel.
+     *
+     * @return NumberOfCacheFiles structure indicating how many files for model and data cache the
+     *     driver needs to cache a single prepared model. It must be less than or equal to
+     *     MAX_NUMBER_OF_CACHE_FILES.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     */
+    NumberOfCacheFiles getNumberOfCacheFilesNeeded();
+
+    /**
+     * Gets information about extensions supported by the driver implementation.
+     *
+     * All extension operations and operands must be fully supported for the extension to appear in
+     * the list of supported extensions.
+     *
+     * @return A list of supported extensions.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     */
+    Extension[] getSupportedExtensions();
+
+    /**
+     * Gets the supported operations in a model.
+     *
+     * getSupportedOperations indicates which operations of the top-level subgraph are fully
+     * supported by the vendor driver. If an operation may not be supported for any reason,
+     * getSupportedOperations must return false for that operation.
+     *
+     * The {@link OperationType::IF} and {@link OperationType::WHILE} operations may only be fully
+     * supported if the vendor driver fully supports all operations in the referenced subgraphs.
+     *
+     * @param model A model whose operations -- and their corresponding operands -- are to be
+     *              verified by the driver.
+     * @return A list of supported operations, where true indicates the operation is supported and
+     *     false indicates the operation is not supported. The index of "supported" corresponds with
+     *     the index of the operation it is describing in the main subgraph.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if provided model is invalid
+     */
+    boolean[] getSupportedOperations(in Model model);
+
+    /**
+     * Get the type of a given device.
+     *
+     * The device type can be used to help application developers to distribute Machine Learning
+     * workloads and other workloads such as graphical rendering. E.g., for an app which renders AR
+     * scenes based on real time object detection results, the developer could choose an ACCELERATOR
+     * type device for ML workloads, and reserve GPU for graphical rendering.
+     *
+     * @return The DeviceType of the device. Please note, this is not a bitfield of DeviceTypes.
+     *     Each device must only be of a single DeviceType.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if the query resulted in an unspecified error
+     */
+    DeviceType getType();
+
+    /**
+     * Get the version string of the driver implementation.
+     *
+     * The version string must be a unique token among the set of version strings of drivers of a
+     * specific device. The token identifies the device driver's implementation. The token must not
+     * be confused with the feature level which is solely defined by the interface version. This API
+     * is opaque to the Android framework, but the Android framework may use the information for
+     * debugging or to pass on to NNAPI applications.
+     *
+     * Application developers sometimes have specific requirements to ensure good user experiences,
+     * and they need more information to make intelligent decisions when the Android framework
+     * cannot. For example, combined with the device name and other information, the token can help
+     * NNAPI applications filter devices based on their needs:
+     *     - An application demands a certain level of performance, but a specific version of the
+     *       driver cannot meet that requirement because of a performance regression.
+     *       The application can disallow the driver based on the version provided.
+     *     - An application has a minimum precision requirement, but certain versions of
+     *       the driver cannot meet that requirement because of bugs or certain optimizations.
+     *       The application can filter out versions of these drivers.
+     *
+     * @return The version string of the device implementation. Must have nonzero length.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if the query resulted in an unspecified error
+     */
+    String getVersionString();
+
+    /**
+     * Asynchronously creates a prepared model for execution and optionally saves it into cache
+     * files.
+     *
+     * prepareModel is used to make any necessary transformations to or alternative representations
+     * to a model for execution, possibly including transformations on the constant data,
+     * optimization on the model's graph, or compilation into the device's native binary format. The
+     * model itself is not changed.
+     *
+     * Optionally, caching information may be provided for the driver to save the prepared model to
+     * cache files for faster model compilation time when the same model preparation is requested in
+     * the future. There are two types of cache file descriptors provided to the driver: model cache
+     * and data cache. For more information on the two types of cache, refer to
+     * getNumberOfCacheFilesNeeded.
+     *
+     * The file descriptors must be opened with read and write permission. A file may have any size,
+     * and the corresponding file descriptor may have any offset. The driver must truncate a file to
+     * zero size before writing to that file. The file descriptors may be closed by the client once
+     * the asynchronous preparation has finished. The driver must dup a file descriptor if it wants
+     * to get access to the cache file later.
+     *
+     * The model is prepared asynchronously with respect to the caller. The prepareModel function
+     * must verify the inputs to the preparedModel function related to preparing the model (as
+     * opposed to saving the prepared model to cache) are correct. If there is an error,
+     * prepareModel must immediately invoke the callback with the appropriate ErrorStatus value and
+     * nullptr for the IPreparedModel, then return a status with a service specific exception with
+     * the same ErrorStatus. If the inputs to the prepareModel function that are related to
+     * preparing the model are valid and there is no error, prepareModel must launch an asynchronous
+     * task to prepare the model in the background, and immediately return from prepareModel. If the
+     * asynchronous task fails to launch, prepareModel must immediately invoke the callback with
+     * ErrorStatus::GENERAL_FAILURE and nullptr for the IPreparedModel, then return a service
+     * specific exception with ErrorStatus::GENERAL_FAILURE.
+     *
+     * When the asynchronous task has finished preparing the model, it must immediately invoke the
+     * callback function provided as an input to prepareModel. If the model was prepared
+     * successfully, the callback object must be invoked with an error status of ErrorStatus::NONE
+     * and the produced IPreparedModel object. If an error occurred preparing the model, the
+     * callback object must be invoked with the appropriate ErrorStatus value and nullptr for the
+     * IPreparedModel.
+     *
+     * The model is prepared with a priority. This priority is relative to other prepared models
+     * owned by the same client. Higher priority executions may use more compute resources than
+     * lower priority executions, and may preempt or starve lower priority executions.
+     *
+     * prepareModel can be called with an optional deadline. If the model is not able to be prepared
+     * before the provided deadline, the model preparation may be aborted, and either
+     * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
+     * sent the same way as other errors, described above.
+     *
+     * Optionally, the driver may save the prepared model to cache during the asynchronous
+     * preparation. Any error that occurs when saving to cache must not affect the status of
+     * preparing the model. Even if the input arguments related to the cache may be invalid, or the
+     * driver may fail to save to cache, the prepareModel function must finish preparing the model.
+     * The driver may choose not to save to cache even if the caching information is provided and
+     * valid.
+     *
+     * The only information that may be unknown to the model at this stage is the shape of the
+     * tensors, which may only be known at execution time. As such, some driver services may return
+     * partially prepared models, where the prepared model may only be finished when it is paired
+     * with a set of inputs to the model. Note that the same prepared model object may be used with
+     * different shapes of inputs on different (possibly concurrent) executions.
+     *
+     * Multiple threads may call prepareModel on the same model concurrently.
+     *
+     * @param model The model to be prepared for execution.
+     * @param preference Indicates the intended execution behavior of a prepared model.
+     * @param priority The priority of the prepared model relative to other prepared models owned by
+     *                 the client.
+     * @param deadlineNs The time by which the model is expected to be prepared. The time is
+     *                   measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+     *                   &ts) or ::android::base::boot_clock). If the model cannot be prepared by
+     *                   the deadline, the preparation may be aborted. Passing -1 means the deadline
+     *                   is omitted. Other negative values are invalid.
+     * @param modelCache A vector of file descriptors for the security-sensitive cache. The length
+     *                   of the vector must either be 0 indicating that caching information is not
+     *                   provided, or match the numModelCache returned from
+     *                   getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in
+     *                   the same order when retrieving the preparedModel from cache files with
+     *                   prepareModelFromCache.
+     * @param dataCache A vector of file descriptors for the constants' cache. The length of the
+     *                  vector must either be 0 indicating that caching information is not provided,
+     *                  or match the numDataCache returned from getNumberOfCacheFilesNeeded. The
+     *                  cache file descriptors will be provided in the same order when retrieving
+     *                  the preparedModel from cache files with prepareModelFromCache.
+     * @param token A caching token of length BYTE_SIZE_OF_CACHE_TOKEN identifying the prepared
+     *              model. The same token will be provided when retrieving the prepared model from
+     *              the cache files with prepareModelFromCache.  Tokens should be chosen to have a
+     *              low rate of collision for a particular application. The driver cannot detect a
+     *              collision; a collision will result in a failed execution or in a successful
+     *              execution that produces incorrect output values. If both modelCache and
+     *              dataCache are empty indicating that caching information is not provided, this
+     *              token must be ignored.
+     * @param callback A callback object used to return the error status of preparing the model for
+     *                 execution and the prepared model if successful, nullptr otherwise. The
+     *                 callback object's notify function must be called exactly once, even if the
+     *                 model could not be prepared.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments related to preparing the model is
+     *       invalid
+     *     - MISSED_DEADLINE_* if the preparation is aborted because the model cannot be prepared by
+     *       the deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    void prepareModel(in Model model, in ExecutionPreference preference, in Priority priority,
+            in long deadlineNs, in ParcelFileDescriptor[] modelCache,
+            in ParcelFileDescriptor[] dataCache, in byte[] token,
+            in IPreparedModelCallback callback);
+
+    /**
+     * Creates a prepared model from cache files for execution.
+     *
+     * prepareModelFromCache is used to retrieve a prepared model directly from cache files to avoid
+     * slow model compilation time. There are two types of cache file descriptors provided to the
+     * driver: model cache and data cache. For more information on the two types of cache files,
+     * refer to getNumberOfCacheFilesNeeded.
+     *
+     * The file descriptors must be opened with read and write permission. A file may have any size,
+     * and the corresponding file descriptor may have any offset. The driver must truncate a file to
+     * zero size before writing to that file. The file descriptors may be closed by the client once
+     * the asynchronous preparation has finished. The driver must dup a file descriptor if it wants
+     * to get access to the cache file later.
+     *
+     * The model is prepared asynchronously with respect to the caller. The prepareModelFromCache
+     * function must verify the inputs to the prepareModelFromCache function are correct, and that
+     * the security-sensitive cache has not been modified since it was last written by the driver.
+     * If there is an error, or if compilation caching is not supported, or if the
+     * security-sensitive cache has been modified, prepareModelFromCache must immediately invoke the
+     * callback with the appropriate ErrorStatus value and nullptr for the IPreparedModel, then
+     * return a status with a service specific exception with the same ErrorStatus. If the inputs to
+     * the prepareModelFromCache function are valid, the security-sensitive cache is not modified,
+     * and there is no error, prepareModelFromCache must launch an asynchronous task to prepare the
+     * model in the background, and immediately return from prepareModelFromCache. If the
+     * asynchronous task fails to launch, prepareModelFromCache must immediately invoke the callback
+     * with ErrorStatus::GENERAL_FAILURE and nullptr for the IPreparedModel, then return a service
+     * specific exception with ErrorStatus::GENERAL_FAILURE.
+     *
+     * When the asynchronous task has finished preparing the model, it must immediately invoke the
+     * callback function provided as an input to prepareModelFromCache. If the model was prepared
+     * successfully, the callback object must be invoked with an error status of ErrorStatus::NONE
+     * and the produced IPreparedModel object. If an error occurred preparing the model, the
+     * callback object must be invoked with the appropriate ErrorStatus value and nullptr for the
+     * IPreparedModel.
+     *
+     * prepareModelFromCache can be called with an optional deadline. If the model is not able to
+     * prepared before the provided deadline, the model preparation may be aborted, and either
+     * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or
+     * {@link ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort
+     * must be sent the same way as other errors, described above.
+     *
+     * The only information that may be unknown to the model at this stage is the shape of the
+     * tensors, which may only be known at execution time. As such, some driver services may return
+     * partially prepared models, where the prepared model may only be finished when it is paired
+     * with a set of inputs to the model. Note that the same prepared model object may be used with
+     * different shapes of inputs on different (possibly concurrent) executions.
+     *
+     * @param deadlineNs The time by which the model is expected to be prepared. The time is
+     *                   measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+     *                   &ts) or ::android::base::boot_clock). If the model cannot be prepared by
+     *                   the deadline, the preparation may be aborted. Passing -1 means the deadline
+     *                   is omitted. Other negative values are invalid.
+     * @param modelCache A vector of file descriptors for the security-sensitive cache. The length
+     *                   of the vector must match the numModelCache returned from
+     *                   getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in
+     *                   the same order as with prepareModel.
+     * @param dataCache A vector of file descriptors for the constants' cache. The length of the
+     *                  vector must match the numDataCache returned from
+     *                  getNumberOfCacheFilesNeeded. The cache file descriptors will be provided in
+     *                  the same order as with prepareModel.
+     * @param token A caching token of length BYTE_SIZE_OF_CACHE_TOKEN identifying the prepared
+     *              model. It is the same token provided when saving the cache files with
+     *              prepareModel. Tokens should be chosen to have a low rate of collision for a
+     *              particular application. The driver cannot detect a collision; a collision will
+     *              result in a failed execution or in a successful execution that produces
+     *              incorrect output values.
+     * @param callback A callback object used to return the error status of preparing the model for
+     *                 execution and the prepared model if successful, nullptr otherwise. The
+     *                 callback object's notify function must be called exactly once, even if the
+     *                 model could not be prepared.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if caching is not supported or if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid
+     *     - MISSED_DEADLINE_* if the preparation is aborted because the model cannot be prepared by
+     *       the deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    void prepareModelFromCache(in long deadlineNs, in ParcelFileDescriptor[] modelCache,
+            in ParcelFileDescriptor[] dataCache, in byte[] token,
+            in IPreparedModelCallback callback);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
new file mode 100644
index 0000000..cb6db11
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.Timing;
+
+/**
+ * IFencedExecutionCallback can be used to query the error status result and duration information
+ * from an IPreparedModel::executeFenced call.
+ */
+@VintfStability
+interface IFencedExecutionCallback {
+    /**
+     * The getExecutionInfo method is used by the clients to query error status result and duration
+     * information. The method must only be called after the actual evaluation has finished or
+     * resulted in an runtime error, as indicated by the status of the sync fence returned by the
+     * IPreparedModel::executeFenced call, otherwise GENERAL_FAILURE must be returned.
+     *
+     * @param out timingLaunched The duration starts when executeFenced is called and ends when
+     *                           executeFenced signals the returned syncFence. Unless measureTiming
+     *                           was set to true when launching the execution and status is NONE,
+     *                           all times must be reported as -1. A driver may choose to report any
+     *                           time as -1, indicating that particular measurement is not
+     *                           available.
+     * @param out timingFenced The duration starts when all waitFor sync fences have been signaled
+     *                         and ends when executeFenced signals the returned syncFence. Unless
+     *                         measureTiming was set to true when launching the execution and status
+     *                         is NONE, all times must be reported as -1. A driver may choose to
+     *                         report any time as -1, indicating that particular measurement is not
+     *                         available.
+     * @return Error status returned from the asynchronously dispatched execution must be:
+     *     - NONE if the asynchronous execution was successful
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if the asynchronous task resulted in an unspecified error
+     *     - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
+     *       deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    ErrorStatus getExecutionInfo(out Timing timingLaunched, out Timing timingFenced);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
new file mode 100644
index 0000000..956b626
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModel.aidl
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.common.NativeHandle;
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.ExecutionResult;
+import android.hardware.neuralnetworks.FencedExecutionResult;
+import android.hardware.neuralnetworks.IBurst;
+import android.hardware.neuralnetworks.Request;
+
+/**
+ * IPreparedModel describes a model that has been prepared for execution and is used to launch
+ * executions.
+ */
+@VintfStability
+interface IPreparedModel {
+    /**
+     * Each {@link OperationType::WHILE} operation in the model has an implicit execution timeout
+     * duration associated with it ("loop timeout duration"). This duration is configurable on a
+     * per-execution basis and must not exceed 15 seconds. The default value is 2 seconds. The units
+     * are nanoseconds.
+     */
+    const long DEFAULT_LOOP_TIMEOUT_DURATION_NS = 2000000000;
+    const long MAXIMUM_LOOP_TIMEOUT_DURATION_NS = 15000000000;
+
+    /**
+     * Performs a synchronous execution on a prepared model.
+     *
+     * The execution is performed synchronously with respect to the caller. executeSynchronously
+     * must verify the inputs to the function are correct, and the usages of memory pools allocated
+     * by IDevice::allocate are valid. If there is an error, executeSynchronously must immediately
+     * return a service specific exception with the appropriate ErrorStatus value. If the inputs to
+     * the function are valid and there is no error, executeSynchronously must perform the
+     * execution, and must not return until the execution is complete.
+     *
+     * The caller must not change the content of any data object referenced by 'request' (described
+     * by the {@link DataLocation} of a {@link RequestArgument}) until executeSynchronously returns.
+     * executeSynchronously must not change the content of any of the data objects corresponding to
+     * 'request' inputs.
+     *
+     * If the prepared model was prepared from a model wherein all tensor operands have fully
+     * specified dimensions, and the inputs to the function are valid, and at execution time every
+     * operation's input operands have legal values, then the execution should complete
+     * successfully: there must be no failure unless the device itself is in a bad state.
+     *
+     * executeSynchronously may be called with an optional deadline. If the execution is not able to
+     * be completed before the provided deadline, the execution may be aborted, and either
+     * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
+     * sent the same way as other errors, described above.
+     *
+     * Any number of calls to the execute* functions, in any combination, may be made concurrently,
+     * even on the same IPreparedModel object.
+     *
+     * @param request The input and output information on which the prepared model is to be
+     *                executed.
+     * @param measure Specifies whether or not to measure duration of the execution. The duration
+     *                runs from the time the driver sees the call to the executeSynchronously
+     *                function to the time the driver returns from the function.
+     * @param deadlineNs The time by which the execution is expected to complete. The time is
+     *                   measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+     *                   &ts) or ::android::base::boot_clock). If the execution cannot be finished
+     *                   by the deadline, the execution may be aborted. Passing -1 means the
+     *                   deadline is omitted. Other negative values are invalid.
+     * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+     *                              executing a {@link OperationType::WHILE} operation. If a loop
+     *                              condition model does not output false within this duration, the
+     *                              execution must be aborted. If -1 is provided, the maximum amount
+     *                              of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+     *                              negative values are invalid. When provided, the duration must
+     *                              not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+     * @return ExecutionResult parcelable, containing the status of the execution, output shapes and
+     *     timing information.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid
+     *     - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
+     *       deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    ExecutionResult executeSynchronously(in Request request, in boolean measureTiming,
+            in long deadlineNs, in long loopTimeoutDurationNs);
+
+    /**
+     * Launch a fenced asynchronous execution on a prepared model.
+     *
+     * The execution is performed asynchronously with respect to the caller. executeFenced must
+     * verify the inputs to the function are correct, and the usages of memory pools allocated by
+     * IDevice::allocate are valid. If there is an error, executeFenced must immediately return a
+     * service specific exception with the corresponding ErrorStatus. If the inputs to the function
+     * are valid and there is no error, executeFenced must dispatch an asynchronous task to perform
+     * the execution in the background, assign a sync fence that will be signaled once the execution
+     * is completed and immediately return a callback that can be used by the client to query the
+     * duration and runtime error status. If the task has finished before the call returns,
+     * syncFence file descriptor may be set to -1. The execution must wait for all the sync fences
+     * (if any) in waitFor to be signaled before starting the actual execution.
+     *
+     * When the asynchronous task has finished its execution, it must immediately signal the
+     * syncFence returned from the executeFenced call. After the syncFence is signaled, the task
+     * must not modify the content of any data object referenced by 'request' (described by the
+     * {@link DataLocation} of a {@link RequestArgument}).
+     *
+     * executeFenced may be called with an optional deadline and an optional duration. If the
+     * execution is not able to be completed before the provided deadline or within the timeout
+     * duration (measured from when all sync fences in waitFor are signaled), whichever comes
+     * earlier, the execution may be aborted, and either
+     * {@link ErrorStatus::MISSED_DEADLINE_TRANSIENT} or {@link
+     * ErrorStatus::MISSED_DEADLINE_PERSISTENT} may be returned. The error due to an abort must be
+     * sent the same way as other errors, described above.
+     *
+     * If any of the sync fences in waitFor changes to error status after the executeFenced call
+     * succeeds, or the execution is aborted because it cannot finish before the deadline has been
+     * reached or the duration has elapsed, the driver must immediately set the returned syncFence
+     * to error status.
+     *
+     * Any number of calls to the execute* functions, in any combination, may be made concurrently,
+     * even on the same IPreparedModel object.
+     *
+     * @param request The input and output information on which the prepared model is to be
+     *                executed. The outputs in the request must have fully specified dimensions.
+     * @param waitFor A vector of sync fence file descriptors. Execution must not start until all
+     *                sync fences have been signaled.
+     * @param measure Specifies whether or not to measure duration of the execution.
+     * @param deadlineNs The time by which the execution is expected to complete. The time is
+     *                   measured in nanoseconds since boot (as from clock_gettime(CLOCK_BOOTTIME,
+     *                   &ts) or ::android::base::boot_clock). If the execution cannot be finished
+     *                   by the deadline, the execution may be aborted. Passing -1 means the
+     *                   deadline is omitted. Other negative values are invalid.
+     * @param loopTimeoutDurationNs The maximum amount of time in nanoseconds that should be spent
+     *                              executing a {@link OperationType::WHILE} operation. If a loop
+     *                              condition model does not output false within this duration, the
+     *                              execution must be aborted. If -1 is provided, the maximum amount
+     *                              of time is {@link DEFAULT_LOOP_TIMEOUT_DURATION_NS}. Other
+     *                              negative values are invalid. When provided, the duration must
+     *                              not exceed {@link MAXIMUM_LOOP_TIMEOUT_DURATION_NS}.
+     * @param durationNs The length of time in nanoseconds within which the execution is expected to
+     *                   complete after all sync fences in waitFor are signaled. If the execution
+     *                   cannot be finished within the duration, the execution may be aborted.
+     *                   Passing -1 means the duration is omitted. Other negative values are
+     *                   invalid.
+     * @return The FencedExecutionResult parcelable, containing IFencedExecutionCallback and the
+     *         sync fence.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if one of the input arguments is invalid, including fences in error
+     *       states.
+     *     - MISSED_DEADLINE_* if the execution is aborted because it cannot be completed by the
+     *       deadline
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    FencedExecutionResult executeFenced(in Request request, in ParcelFileDescriptor[] waitFor,
+            in boolean measureTiming, in long deadlineNs, in long loopTimeoutDurationNs,
+            in long durationNs);
+
+    /**
+     * Configure a Burst object used to execute multiple inferences on a prepared model in rapid
+     * succession.
+     *
+     * If the prepared model was prepared from a model wherein all tensor operands have fully
+     * specified dimensions, and a valid serialized Request is sent to the Burst for execution, and
+     * at execution time every operation's input operands have legal values, then the execution
+     * should complete successfully (ErrorStatus::NONE): There must be no failure unless the device
+     * itself is in a bad state.
+     *
+     * @return burst Execution burst controller object.
+     * @throws ServiceSpecificException with one of the following ErrorStatus values:
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     */
+    IBurst configureExecutionBurst();
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
new file mode 100644
index 0000000..29cca6d
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelCallback.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ErrorStatus;
+import android.hardware.neuralnetworks.IPreparedModel;
+
+/**
+ * IPreparedModelCallback must be used to return a prepared model produced by an asynchronous task
+ * launched from IDevice::prepareModel*.
+ */
+@VintfStability
+interface IPreparedModelCallback {
+    /**
+     * Notify must be invoked immediately after the asynchronous task holding this callback has
+     * finished preparing the model. If the model was successfully prepared, the method must be
+     * invoked with ErrorStatus::NONE and the prepared model. If the model was not able to be
+     * successfully prepared, the method must be invoked with the appropriate ErrorStatus and
+     * nullptr as the IPreparedModel. If the asynchronous task holding this callback fails to launch
+     * or if the model provided to IDevice::prepareModel is invalid, notify method must be invoked
+     * with the appropriate error as well as nullptr for the IPreparedModel.
+     *
+     * @param status Error status returned from the asynchronous model preparation task; must be:
+     *               - NONE if the asynchronous task successfully prepared the model
+     *               - DEVICE_UNAVAILABLE if driver is offline or busy
+     *               - GENERAL_FAILURE if the asynchronous task resulted in an unspecified error
+     *               - INVALID_ARGUMENT if one of the input arguments to prepareModel is invalid
+     *               - MISSED_DEADLINE_* if the preparation is aborted because the model cannot be
+     *                 prepared by the deadline
+     *               - RESOURCE_EXHAUSTED_* if the task was aborted by the driver
+     * @param preparedModel A model that has been asynchronously prepared for execution. If the
+     *                      model was unable to be prepared due to an error, nullptr must be passed
+     *                      in place of the IPreparedModel object.
+     */
+    void notify(in ErrorStatus status, in IPreparedModel preparedModel);
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
new file mode 100644
index 0000000..878b0ad
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/IPreparedModelParcel.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.IPreparedModel;
+
+/**
+ * A parcelable for passing a vector of IPreparedModel objects.
+ */
+@VintfStability
+parcelable IPreparedModelParcel {
+    IPreparedModel preparedModel;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
new file mode 100644
index 0000000..244ac87
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Memory.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.common.Ashmem;
+import android.hardware.common.MappableFile;
+import android.hardware.graphics.common.HardwareBuffer;
+
+/**
+ * The different types of memory that can be shared across processes.
+ */
+@VintfStability
+union Memory {
+    /**
+     * Ashmem hidl_memory type from HIDL.
+     */
+    Ashmem ashmem;
+    /**
+     * File that can be mapped.
+     */
+    MappableFile mappableFile;
+    /**
+     * AIDL representation of AHardwareBuffer.
+     */
+    HardwareBuffer hardwareBuffer;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl
new file mode 100644
index 0000000..2f11dec
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Model.aidl
@@ -0,0 +1,69 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.ExtensionNameAndPrefix;
+import android.hardware.neuralnetworks.Memory;
+import android.hardware.neuralnetworks.Subgraph;
+
+/**
+ * A Neural Network Model.
+ *
+ * This includes not only the execution graph, but also constant data such as weights or scalars
+ * added at construction time. The only information that may not be known is the shape of the input
+ * tensors.
+ */
+@VintfStability
+parcelable Model {
+    /**
+     * The top-level subgraph.
+     */
+    Subgraph main;
+    /**
+     * Referenced subgraphs.
+     *
+     * Each subgraph is referenced by the main subgraph or at least one other referenced subgraph.
+     *
+     * There must be no reference cycles.
+     */
+    Subgraph[] referenced;
+    /**
+     * A byte buffer containing operand data that were copied into the model.
+     *
+     * An operand's value must be located here if and only if Operand::lifetime equals
+     * OperandLifeTime::CONSTANT_COPY.
+     */
+    byte[] operandValues;
+    /**
+     * A collection of shared memory pools containing operand values.
+     *
+     * An operand's value must be located here if and only if Operand::lifetime equals
+     * OperandLifeTime::CONSTANT_POOL.
+     */
+    Memory[] pools;
+    /**
+     * 'true' indicates TENSOR_FLOAT32 may be calculated with range and/or precision as low as that
+     * of the IEEE 754 16-bit floating-point format.
+     * 'false' indicates TENSOR_FLOAT32 must be calculated using at least the range and precision of
+     * the IEEE 754 32-bit floating-point format.
+     */
+    boolean relaxComputationFloat32toFloat16;
+    /**
+     * The mapping between extension names and prefixes of operand and operation type values.
+     */
+    ExtensionNameAndPrefix[] extensionNameToPrefix;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
new file mode 100644
index 0000000..1ca2676
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/NumberOfCacheFiles.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Structure indicating how many files for model and numDataCache cache the driver needs to cache a
+ * single prepared model.
+ */
+@VintfStability
+parcelable NumberOfCacheFiles {
+    int numModelCache;
+    int numDataCache;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
new file mode 100644
index 0000000..998e06d
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operand.aidl
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.DataLocation;
+import android.hardware.neuralnetworks.OperandExtraParams;
+import android.hardware.neuralnetworks.OperandLifeTime;
+import android.hardware.neuralnetworks.OperandType;
+
+/**
+ * Describes one operand of the model's graph.
+ */
+@VintfStability
+parcelable Operand {
+    /**
+     * The data type.
+     *
+     * Besides the values listed in {@link OperandType}, any value above
+     * {@link IDevice::OPERAND_TYPE_BASE_MAX} is possible and should be interpreted as an extension
+     * type according to {@link Model::extensionNameToPrefix}.
+     */
+    OperandType type = OperandType.FLOAT32;
+    /**
+     * Dimensions of the operand.
+     *
+     * For a scalar operand, dimensions.size() must be 0.
+     *
+     * A tensor operand with all dimensions specified has "fully specified" dimensions. Whenever
+     * possible (i.e., whenever the dimensions are known at model construction time), a tensor
+     * operand should have (but is not required to have) fully specified dimensions, in order to
+     * enable the best possible performance.
+     *
+     * If a tensor operand's dimensions are not fully specified, the dimensions of the operand are
+     * deduced from the operand dimensions and values of the operation for which that operand is an
+     * output or from the corresponding {@link OperationType::IF} or {@link OperationType::WHILE}
+     * operation input operand dimensions in the case of referenced subgraph input operands.
+     *
+     * In the following situations, a tensor operand's dimensions must be fully specified:
+     *
+     *     . The operand has lifetime CONSTANT_COPY or CONSTANT_POOL.
+     *
+     *     . The operand has lifetime SUBGRAPH_INPUT and belongs to the main subgraph. Fully
+     *       specified dimensions must either be present in the Operand or they must be provided in
+     *       the corresponding RequestArgument.
+     *       EXCEPTION: If the input is optional and omitted (by setting the hasNoValue field of the
+     *       corresponding RequestArgument to true) then it need not have fully specified
+     *       dimensions.
+     *
+     * A tensor operand with some number of unspecified dimensions is represented by setting each
+     * unspecified dimension to 0.
+     *
+     * A tensor operand with unspecified rank is represented by providing an empty dimensions
+     * vector.
+     */
+    int[] dimensions;
+    /**
+     * Quantized scale of the operand.
+     *
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
+     */
+    float scale;
+    /**
+     * Quantized zero-point offset of the operand.
+     *
+     * Must be 0 when not applicable to an operand type.
+     *
+     * See {@link OperandType}.
+     */
+    int zeroPoint;
+    /**
+     * How the operand is used.
+     */
+    OperandLifeTime lifetime = OperandLifeTime.TEMPORARY_VARIABLE;
+    /**
+     * Where to find the data for this operand.
+     * If the lifetime is TEMPORARY_VARIABLE, SUBGRAPH_INPUT, SUBGRAPH_OUTPUT, or NO_VALUE:
+     * - All the fields must be 0.
+     * If the lifetime is CONSTANT_COPY:
+     * - location.poolIndex is 0.
+     * - location.offset is the offset in bytes into Model.operandValues.
+     * - location.length is set.
+     * If the lifetime is CONSTANT_POOL:
+     * - location.poolIndex is set.
+     * - location.offset is the offset in bytes into the specified pool.
+     * - location.length is set.
+     * If the lifetime is SUBGRAPH:
+     * - location.poolIndex is 0.
+     * - location.offset is the index of the referenced subgraph in {@link Model::referenced}.
+     * - location.length is 0.
+     */
+    DataLocation location;
+    /**
+     * Additional parameters specific to a particular operand type.
+     */
+    @nullable OperandExtraParams extraParams;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl
new file mode 100644
index 0000000..229754a
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandExtraParams.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.SymmPerChannelQuantParams;
+
+/**
+ * Parameters specific to a particular operand type.
+ */
+@VintfStability
+union OperandExtraParams {
+    /**
+     * Symmetric per-channel quantization parameters.
+     *
+     * Only applicable to operands of type TENSOR_QUANT8_SYMM_PER_CHANNEL.
+     */
+    SymmPerChannelQuantParams channelQuant;
+    /**
+     * Extension operand parameters.
+     *
+     * The framework treats this as an opaque data blob.
+     * The format is up to individual extensions.
+     */
+    byte[] extension;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl
new file mode 100644
index 0000000..1d18149
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandLifeTime.aidl
@@ -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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * How an operand is used.
+ */
+@VintfStability
+@Backing(type="int")
+enum OperandLifeTime {
+    /**
+     * The operand is internal to the model. It's created by an operation and consumed by other
+     * operations. It must be an output operand of exactly one operation.
+     */
+    TEMPORARY_VARIABLE,
+    /**
+     * The operand is an input of a subgraph. It must not be an output operand of any operation.
+     *
+     * An operand can't be both input and output of a subgraph.
+     */
+    SUBGRAPH_INPUT,
+    /**
+     * The operand is an output of a subgraph. It must be an output operand of exactly one
+     * operation.
+     *
+     * An operand can't be both input and output of a subgraph.
+     */
+    SUBGRAPH_OUTPUT,
+    /**
+     * The operand is a constant found in Model.operandValues. It must not be an output operand of
+     * any operation.
+     */
+    CONSTANT_COPY,
+    /**
+     * The operand is a constant that was specified via a Memory object. It must not be an output
+     * operand of any operation.
+     */
+    CONSTANT_POOL,
+    /**
+     * The operand does not have a value. This is valid only for optional arguments of operations.
+     */
+    NO_VALUE,
+    /**
+     * The operand is a reference to a subgraph. It must be an input to one or more
+     * {@link OperationType::IF} or {@link OperationType::WHILE} operations.
+     */
+    SUBGRAPH,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
new file mode 100644
index 0000000..7f53967
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandPerformance.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.OperandType;
+import android.hardware.neuralnetworks.PerformanceInfo;
+
+/**
+ * Driver performance when operating on a particular data type. In the case of float32 data, this is
+ * used when the calculations are not relaxed.
+ */
+@VintfStability
+parcelable OperandPerformance {
+    OperandType type = OperandType.FLOAT32;
+    PerformanceInfo info;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl
new file mode 100644
index 0000000..12edc0f
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperandType.aidl
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Operand types.
+ *
+ * The type of an operand in a model.
+ *
+ * Types prefaced with TENSOR_* must be used for tensor data (i.e., tensors
+ * with at least one dimension). Types not prefaced by TENSOR_* represent
+ * scalar values and must have no dimensions.
+ */
+@VintfStability
+@Backing(type="int")
+enum OperandType {
+    /**
+     * A 32 bit floating point scalar value.
+     */
+    FLOAT32 = 0,
+    /**
+     * A signed 32 bit integer scalar value.
+     */
+    INT32 = 1,
+    /**
+     * An unsigned 32 bit integer scalar value.
+     */
+    UINT32 = 2,
+    /**
+     * A tensor of 32 bit floating point values.
+     */
+    TENSOR_FLOAT32 = 3,
+    /**
+     * A tensor of 32 bit integer values.
+     */
+    TENSOR_INT32 = 4,
+    /**
+     * A tensor of 8 bit unsigned integers that represent real numbers.
+     *
+     * Attached to this tensor are two numbers that can be used to convert the 8 bit integer to the
+     * real value and vice versa. These two numbers are:
+     * - scale: a 32 bit floating point value greater than zero.
+     * - zeroPoint: a 32 bit integer, in range [0, 255].
+     *
+     * The formula is:
+     *   real_value = (integer_value - zeroPoint) * scale.
+     */
+    TENSOR_QUANT8_ASYMM = 5,
+    /**
+     * An 8 bit boolean scalar value.
+     *
+     * Values of this operand type are either true or false. A zero value represents false; any
+     * other value represents true.
+     */
+    BOOL = 6,
+    /**
+     * A tensor of 16 bit signed integers that represent real numbers.
+     *
+     * Attached to this tensor is a number representing real value scale that is used to convert the
+     * 16 bit number to a real value in the following way:
+     * realValue = integerValue * scale.
+     *
+     * scale is a 32 bit floating point with value greater than zero.
+     */
+    TENSOR_QUANT16_SYMM = 7,
+    /**
+     * A tensor of IEEE 754 16 bit floating point values.
+     */
+    TENSOR_FLOAT16 = 8,
+    /**
+     * A tensor of 8 bit boolean values.
+     *
+     * Values of this operand type are either true or false. A zero value represents false; any
+     * other value represents true.
+     */
+    TENSOR_BOOL8 = 9,
+    /**
+     * An IEEE 754 16 bit floating point scalar value.
+     */
+    FLOAT16 = 10,
+    /**
+     * A tensor of 8 bit signed integers that represent real numbers.
+     *
+     * This tensor is associated with additional fields that can be used to convert the 8 bit signed
+     * integer to the real value and vice versa. These fields are:
+     * - channelDim: a 32 bit unsigned integer indicating channel dimension.
+     * - scales: an array of positive 32 bit floating point values.
+     * The size of the scales array must be equal to dimensions[channelDim].
+     *
+     * {@link SymmPerChannelQuantParams} must hold the parameters for an Operand of this type.
+     * The channel dimension of this tensor must not be unknown (dimensions[channelDim] != 0).
+     *
+     * The formula is:
+     * realValue[..., C, ...] =
+     *     integerValue[..., C, ...] * scales[C]
+     * where C is an index in the Channel dimension.
+     */
+    TENSOR_QUANT8_SYMM_PER_CHANNEL = 11,
+    /**
+     * A tensor of 16 bit unsigned integers that represent real numbers.
+     *
+     * Attached to this tensor are two numbers that can be used to convert the 16 bit integer to the
+     * real value and vice versa. These two numbers are:
+     * - scale: a 32 bit floating point value greater than zero.
+     * - zeroPoint: a 32 bit integer, in range [0, 65535].
+     *
+     * The formula is:
+     * real_value = (integer_value - zeroPoint) * scale.
+     */
+    TENSOR_QUANT16_ASYMM = 12,
+    /**
+     * A tensor of 8 bit signed integers that represent real numbers.
+     *
+     * Attached to this tensor is a number representing real value scale that is used to convert the
+     * 8 bit number to a real value in the following way:
+     * realValue = integerValue * scale.
+     *
+     * scale is a 32 bit floating point with value greater than zero.
+     */
+    TENSOR_QUANT8_SYMM = 13,
+    /**
+     * A tensor of 8 bit signed integers that represent real numbers.
+     *
+     * Attached to this tensor are two numbers that can be used to convert the 8 bit integer to the
+     * real value and vice versa. These two numbers are:
+     * - scale: a 32 bit floating point value greater than zero.
+     * - zeroPoint: a 32 bit integer, in range [-128, 127].
+     *
+     * The formula is:
+     * real_value = (integer_value - zeroPoint) * scale.
+     */
+    TENSOR_QUANT8_ASYMM_SIGNED = 14,
+    /**
+     * A reference to a subgraph.
+     *
+     * Must have the lifetime {@link OperandLifeTime::SUBGRAPH}.
+     */
+    SUBGRAPH = 15,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
new file mode 100644
index 0000000..366d9a4
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Operation.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.OperationType;
+
+/**
+ * Describes one operation of the model's graph.
+ */
+@VintfStability
+parcelable Operation {
+    /**
+     * The operation type.
+     *
+     * Besides the values listed in {@link OperationType}, any value above
+     * {@link IDevice::OPERATION_TYPE_BASE_MAX} is possible and should be interpreted as an
+     * extension type according to {@link Model::extensionNameToPrefix}.
+     */
+    OperationType type = OperationType.ADD;
+    /**
+     * Describes the table that contains the indexes of the inputs of the operation. The offset is
+     * the index in the operandIndexes table.
+     */
+    int[] inputs;
+    /**
+     * Describes the table that contains the indexes of the outputs of the operation. The offset is
+     * the index in the operandIndexes table.
+     */
+    int[] outputs;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
new file mode 100644
index 0000000..e7fb90d
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OperationType.aidl
@@ -0,0 +1,5131 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Operation types.
+ *
+ * The type of an operation in a model.
+ */
+@VintfStability
+@Backing(type="int")
+enum OperationType {
+    /**
+     * Adds two tensors, element-wise.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the sum of both input tensors, optionally
+     * modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its
+     * way forward.
+     *
+     * Example:
+     *
+     *     input1.dimension = {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Since HAL version 1.2, generic zero-sized input tensor is supported. Zero
+     * dimension is only compatible with 0 or 1. The size of the output
+     * dimension is zero if either of corresponding input dimension is zero.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scales and zeroPoint can be different from input0 scale and zeroPoint.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
+     *
+     * Outputs:
+     * * 0: The sum, a tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    ADD = 0,
+    /**
+     * Performs a 2-D average pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         sum_{di, dj}(
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+     *         ) / sum(1)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since HAL version 1.2.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    AVERAGE_POOL_2D = 1,
+    /**
+     * Concatenates the input tensors along the given dimension.
+     *
+     * The input tensors must have identical {@link OperandType} and the same
+     * dimensions except the dimension along the concatenation axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *   (full support since HAL version 1.2, see the input section)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0 ~ n-1: The list of n input tensors, of shape
+     *            [D0, D1, ..., Daxis(i), ..., Dm].
+     *            Before HAL version 1.2, all input tensors of
+     *            {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *            must have the same scale and zeroPoint as the output tensor.
+     *            Input tensors of
+     *            {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *            are allowed to have different scale and zeroPoint.
+     *            Since HAL version 1.2, zero-sized tensors are supported.
+     * * n: An {@link OperandType::INT32} scalar, specifying the
+     *      concatenation axis.
+     *
+     * Outputs:
+     * * 0: The output, a tensor of the same {@link OperandType} as the input
+     *      tensors. The output shape is [D0, D1, ..., sum(Daxis(i)), ..., Dm].
+     *      Since HAL version 1.2, for a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scale and zeroPoint values can be different from
+     *      input tensors. Before HAL version 1.2 they have to be the same as for the input tensors.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint values can be different from input tensors.
+     */
+    CONCATENATION = 2,
+    /**
+     * Performs a 2-D convolution operation.
+     *
+     * The CONV_2D op sweeps a 2-D filter that can mix channels together over a
+     * batch of images, applying the filter to each window of each image of the
+     * appropriate size.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         sum_{di, dj, k} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+     *             filter[channel, di, dj, k]
+     *         ) + bias[channel]
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 32 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * Available since HAL version 1.2:
+     * * 16 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Available since HAL version 1.3:
+     * * Quantized signed (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter.
+     *      For tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension (SymmPerChannelQuantParams::channelDim)
+     *      must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32}
+     *      or {@link OperandType::TENSOR_FLOAT16} the bias must be of the same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     * * 11: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 12 (dilation factor for height) must be specified as well.
+     *      Available since HAL version 1.2.
+     * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 11 (dilation factor for width) must be specified as well.
+     *      Available since HAL version 1.2.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter.
+     *      For tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension (SymmPerChannelQuantParams::channelDim)
+     *      must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32}
+     *      or {@link OperandType::TENSOR_FLOAT16} the bias must be of the same
+     *      type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     * * 8: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 9 (dilation factor for height) must be specified as well.
+     *      Available since HAL version 1.2.
+     * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 8 (dilation factor for width) must be specified as well.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out].
+     *      Before HAL version 1.2, for output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the following condition must be satisfied: output_scale > input_scale * filter_scale
+     */
+    CONV_2D = 3,
+    /**
+     * Performs a depthwise 2-D convolution operation.
+     *
+     * Given an input tensor of shape [batches, height, width, depth_in] and a
+     * filter tensor of shape [1, filter_height, filter_width, depth_out]
+     * containing depth_out convolutional filters of depth 1, DEPTHWISE_CONV
+     * applies a different filter to each input channel (expanding from 1
+     * channel to channel_multiplier channels for each), then concatenates the
+     * results together.
+     *
+     * The output has depth_out = depth_in * depth_multiplier channels.
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, k * channel_multiplier + q] =
+     *         sum_{di, dj} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, k] *
+     *             filter[1, di, dj, k * channel_multiplier + q]
+     *         ) + bias[k * channel_multiplier + q]
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 32 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * Available since HAL version 1.2:
+     * * 16 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Available since HAL version 1.3:
+     * * Quantized signed (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+     *      specifying the filter.
+     *      For tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension (SymmPerChannelQuantParams::channelDim)
+     *      must be set to 3.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32}
+     *      or {@link OperandType::TENSOR_FLOAT16} the bias must be of the same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, specifying the depthwise
+     *      multiplier.
+     * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+     *       {@link FusedActivationFunc} values. Specifies the activation to
+     *       invoke on the result.
+     * * 11: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since HAL version 1.2.
+     * * 12: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 13 (dilation factor for height) must be specified as well.
+     *      Available since HAL version 1.2.
+     * * 13: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 12 (dilation factor for width) must be specified as well.
+     *      Available since HAL version 1.2.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape [1, filter_height, filter_width, depth_out],
+     *      specifying the filter.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32}
+     *      or {@link OperandType::TENSOR_FLOAT16} the bias must be of the same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the depthwise
+     *      multiplier.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     * * 9: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for width. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on width dimension. If this input is set,
+     *      input 10 (dilation factor for height) must be specified as well.
+     *      Available since HAL version 1.2.
+     * * 10: An optional {@link OperandType::INT32} scalar, specifying the dilation
+     *      factor for height. Defaults to 1. If set to k > 1, there will be k-1 skipped
+     *      cells between each filter element on height dimension. If this input is set,
+     *      input 9 (dilation factor for width) must be specified as well.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out]. Before HAL version 1.2, for
+     *      output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the following condition must be satisfied:
+     *      output_scale > input_scale * filter_scale
+     */
+    DEPTHWISE_CONV_2D = 4,
+    /**
+     * Rearranges data from depth into blocks of spatial data.
+     *
+     * More specifically, this op outputs a copy of the input tensor where
+     * values from the depth dimension are moved in spatial blocks to the height
+     * and width dimensions. The value block_size indicates the input block size
+     * and how the data is moved.
+     *
+     * Chunks of data of size block_size * block_size from depth are rearranged
+     * into non-overlapping blocks of size block_size x block_size.
+     *
+     * The width of the output tensor is input_depth * block_size, whereas the
+     * height is input_height * block_size. The depth of the input tensor must
+     * be divisible by block_size * block_size
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+     *      block_size must be >=1 and block_size * block_size must be a divisor
+     *      of the input depth.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape [batch, height*block_size,
+     *      width*block_size, depth/(block_size*block_size)].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    DEPTH_TO_SPACE = 5,
+    /**
+     * Dequantizes the input tensor.
+     *
+     * The formula is:
+     *
+     *     output = (input - zeroPoint) * scale.
+     *
+     * Supported input tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported output tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}.
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: A tensor with the same shape as input0.
+     */
+    DEQUANTIZE = 6,
+    /**
+     * Looks up sub-tensors in the input tensor.
+     *
+     * This operator takes for input a tensor of values (Values) and
+     * a one-dimensional tensor of selection indices (Lookups).
+     * The output tensor is the concatenation of sub-tensors of Values as
+     * selected by Lookups.
+     *
+     * Think of Values as being sliced along its first dimension:
+     * The entries in Lookups select which slices are concatenated together
+     * to create the output tensor.
+     *
+     * For example, if Values has shape of [40, 200, 300] and
+     * Lookups has shape of [3], all three values found in Lookups are
+     * expected to be between 0 and 39. The resulting tensor must
+     * have shape of [3, 200, 300].
+     *
+     * If a value in Lookups is out of bounds, the operation must fail
+     * and an error must be reported.
+     *
+     * Supported value tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported value tensor rank: from 2
+     *
+     * Inputs:
+     * * 0: Lookups. A 1-D tensor of {@link OperandType::TENSOR_INT32}.
+     *      The values are indices into the first dimension of Values.
+     * * 1: Values. An n-D tensor, where n >= 2, from which sub-tensors are
+     *      extracted.
+     *
+     * Output:
+     * * 0: A n-D tensor with the same rank and shape as the Values
+     *      tensor, except for the first dimension which has the same size
+     *      as Lookups' only dimension.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input1.
+     */
+    EMBEDDING_LOOKUP = 7,
+    /**
+     * Computes element-wise floor() on the input tensor.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor, of the same {@link OperandType} and dimensions as
+     *      the input tensor.
+     */
+    FLOOR = 8,
+    /**
+     * Denotes a fully (densely) connected layer, which connects all elements
+     * in the input tensor with each element in the output tensor.
+     *
+     * This layer implements the operation:
+     *
+     *     outputs = activation(inputs * weights’ + bias)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor of at least rank 2, specifying the input. If rank is
+     *      greater than 2, then it gets flattened to a 2-D Tensor. The
+     *      (flattened) 2-D Tensor is reshaped (if necessary) to
+     *      [batch_size, input_size], where "input_size" corresponds to the
+     *      number of inputs to the layer, matching the second dimension of
+     *      weights, and "batch_size" is calculated by dividing the number of
+     *      elements by "input_size".
+     *      Since HAL version 1.2, zero batch_size is supported for this tensor.
+     * * 1: A 2-D tensor, specifying the weights, of shape
+     *      [num_units, input_size], where "num_units" corresponds to the number
+     *      of output nodes.
+     * * 2: A 1-D tensor, of shape [num_units], specifying the bias. For input
+     *      tensor of {@link OperandType::TENSOR_FLOAT32}, the bias should
+     *      also be of {@link OperandType::TENSOR_FLOAT32}.
+     *      For input tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32},
+     *      with zeroPoint of 0 and bias_scale == input_scale * filter_scale.
+     * * 3: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *
+     * Outputs:
+     * * 0: The output tensor, of shape [batch_size, num_units]. Before HAL version 1.2, for
+     *      output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}, the following
+     *      condition must be satisfied: output_scale > input_scale * filter_scale.
+     */
+    FULLY_CONNECTED = 9,
+    /**
+     * Looks up sub-tensors in the input tensor using a key-value map.
+     *
+     * This operator takes for input a tensor of values (Values),
+     * a one-dimensional tensor of selection values (Lookups) and
+     * a one-dimensional tensor that maps these values to Values
+     * indexes. The output tensor is the concatenation of sub-tensors of
+     * Values as selected by Lookups via Keys.
+     *
+     * Think of Values as being sliced along its outer-most dimension.
+     * The output is a concatenation of selected slices, with one slice
+     * for each entry of Lookups. The slice selected is the one at the
+     * same index as the Maps entry that matches the value in Lookups.
+     *
+     * For a hit, the corresponding sub-tensor of Values is included
+     * in the Output tensor. For a miss, the corresponding sub-tensor in
+     * Output must have zero values.
+     *
+     * For example, if Values has shape of [40, 200, 300],
+     * Keys should have a shape of [40]. If Lookups tensor has shape
+     * of [3], three slices are being concatenated, so the resulting tensor
+     * must have the shape of [3, 200, 300]. If the first entry in Lookups
+     * has the value 123456, that value must be located in Keys tensor.
+     * If the sixth entry of Keys contains 123456, the sixth slice of Values
+     * must be selected. If no entry in Keys has 123456, a slice of zeroes
+     * must be concatenated.
+     *
+     * Supported value tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported value tensor rank: from 2
+     *
+     * Inputs:
+     * * 0: Lookups. A 1-D {@link OperandType::TENSOR_INT32} tensor with
+     *      shape [ k ].
+     * * 1: Keys. A 1-D {@link OperandType::TENSOR_INT32} tensor with shape
+     *      [ n ]; Keys and Values pair represent a map, i.e., the ith element
+     *      in Keys (Keys[i]) is the key to select the ith sub-tensor in Values
+     *      (Values[i]), where 0 <= i <= n-1. Keys tensor *MUST* be sorted in
+     *      ascending order.
+     * * 2: Values. A tensor with shape of [ n, … ]; i.e., the first dimension
+     *      must be n.
+     *
+     * Outputs:
+     * * 0: Output. A tensor with shape [ k …].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scale and zeroPoint must be the same as input2.
+     * * 1: Hits. A boolean tensor with shape [ k ] indicates whether the lookup
+     *      hits (True) or not (False).
+     *      Stored as {@link OperandType::TENSOR_QUANT8_ASYMM} with offset 0
+     *      and scale 1.0f.
+     *      A non-zero byte represents True, a hit. A zero indicates otherwise.
+     */
+    HASHTABLE_LOOKUP = 10,
+    /**
+     * Applies L2 normalization along the axis dimension.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[batch, row, col, channel] =
+     *         input[batch, row, col, channel] /
+     *         sqrt(sum_{c} pow(input[batch, row, col, c], 2))
+     *
+     * By default the axis dimension is the last dimension of the input tensor.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     * Tensors with rank less than 4 are only supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be normalized.
+     * * 1: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension normalization would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 128 and the zeroPoint must be 128.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the scale must be 1.f / 128 and the zeroPoint must be 0.
+     *
+     *      NOTE: Before HAL version 1.3, if the elements along an axis are all zeros,
+     *      the result is undefined. Since HAL version 1.3, if the elements along an axis
+     *      are all zeros, the result is logical zero.
+     */
+    L2_NORMALIZATION = 11,
+    /**
+     * Performs an 2-D L2 pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, c] =
+     *         sqrt(sum_{di, dj} pow(input[b, strides[1] * i + di, strides[2] * j + dj, c], 2) /
+     *              sum(1))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since HAL version 1.2.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     */
+    L2_POOL_2D = 12,
+    /**
+     * Applies Local Response Normalization along the depth dimension.
+     *
+     * The 4-D input tensor is treated as a 3-D array of 1-D vectors (along the
+     * last dimension), and each vector is normalized independently. Within a
+     * given vector, each component is divided by the weighted, squared sum of
+     * inputs within depth_radius.
+     *
+     * The output is calculated using this formula:
+     *
+     *     sqr_sum[a, b, c, d] = sum(
+     *         pow(input[a, b, c, d - depth_radius : d + depth_radius + 1], 2))
+     *     output = input / pow((bias + alpha * sqr_sum), beta)
+     *
+     * For input tensor with rank less than 4, independently normalizes each
+     * 1-D slice along specified dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     * Tensors with rank less than 4 are only supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the radius of
+     *      the normalization window.
+     * * 2: A scalar, specifying the bias, must not be zero.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the bias
+     *      value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the bias
+     *      value must be of {@link OperandType::FLOAT32}.
+     * * 3: A scalar, specifying the scale factor, alpha.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
+     *      alpha value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
+     *      alpha value must be of {@link OperandType::FLOAT32}.
+     * * 4: A scalar, specifying the exponent, beta.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the beta
+     *      value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the beta
+     *      value must be of {@link OperandType::FLOAT32}.
+     * * 5: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension normalization would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    LOCAL_RESPONSE_NORMALIZATION = 13,
+    /**
+     * Computes sigmoid activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = 1 / (1 + exp(-input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 256 and the zeroPoint must be 0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the scale must be 1.f / 256 and the zeroPoint must be -128.
+     */
+    LOGISTIC = 14,
+    /**
+     * Projects an input to a bit vector via locality senstive hashing.
+     *
+     * Supported input tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *
+     * Supported input tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: Hash functions. Dim.size == 2, DataType: Float.
+     *      Tensor[0].Dim[0]: 15 of hash functions.
+     *      Tensor[0].Dim[1]: 16 of projected output bits generated by each
+     *      hash function.
+     *      If the projection type is Sparse:
+     *      Tensor[0].Dim[1] + ceil(log2(Tensor[0].Dim[0])) <= 32
+     *
+     * * 1: Input. Dim.size >= 1, no restriction on DataType.
+     * * 2: Weight. Optional. Dim.size == 1, DataType: Float.
+     *      If not set, each input element is considered to have the same weight
+     *      of 1.0.
+     *      Tensor[1].Dim[0] == Tensor[2].Dim[0]
+     * * 3: Type:
+     *        Sparse:
+     *          Value LSHProjectionType_SPARSE(=3) (since HAL version 1.2).
+     *          Computed bit vector is considered to be sparse.
+     *          Each output element is an int32 made up of multiple bits
+     *          computed from hash functions.
+     *
+     *          NOTE: To avoid collisions across hash functions, an offset value
+     *          of k * (1 << Tensor[0].Dim[1]) will be added to each signature,
+     *          where k is the index of the hash function.
+     *
+     *          Value LSHProjectionType_SPARSE_DEPRECATED(=1).
+     *          Legacy behavior that does not include the offset value.
+     *
+     *        Dense:
+     *          Value LSHProjectionType_DENSE(=2).
+     *          Computed bit vector is considered to be dense. Each output
+     *          element represents a bit and can take the value of either
+     *          0 or 1.
+     *
+     * Outputs:
+     * * 0: If the projection type is Sparse:
+     *      Output.Dim == { Tensor[0].Dim[0] }
+     *      A tensor of int32 that represents hash signatures.
+     *
+     *      If the projection type is Dense:
+     *      Output.Dim == { Tensor[0].Dim[0] * Tensor[0].Dim[1] }
+     *      A flattened tensor that represents projected bit vectors.
+     * The offset value for sparse projections was added in HAL version 1.2.
+     */
+    LSH_PROJECTION = 15,
+    /**
+     * Performs a single time step in a Long Short-Term Memory (LSTM) layer
+     *
+     * The LSTM operation is described by the following equations.
+     *
+     * \f{eqnarray*}{
+     * i_t =& \sigma(W_{xi}x_t+W_{hi}h_{t-1}+W_{ci}C_{t-1}+b_i) & \\
+     * f_t =& \sigma(W_{xf}x_t+W_{hf}h_{t-1}+W_{cf}C_{t-1}+b_f) & \\
+     * C_t =& clip(f_t \odot C_{t-1} + i_t \odot
+     *        g(W_{xc}x_t+W_{hc}h_{t-1}+b_c),\ t_{cell}) & \\
+     * o_t =& \sigma(W_{xo}x_t+W_{ho}h_{t-1}+W_{co}C_t+b_o) & \\
+     *      & & \\
+     *      & clip(W_{proj}(o_t \odot g(C_t))+b_{proj},\ t_{proj})
+     *      & if\ there\ is\ a\ projection; \\
+     * h_t =& & \\
+     *      & o_t \odot g(C_t) & otherwise. \\
+     * \f}
+     * Where:
+     * * \f$x_t\f$ is the input,
+     * * \f$i_t\f$ is the input gate,
+     * * \f$f_t\f$ is the forget gate,
+     * * \f$C_t\f$ is the cell state,
+     * * \f$o_t\f$ is the output,
+     * * \f$h_t\f$ is the output state,
+     * * \f$\sigma\f$ is the logistic sigmoid function,
+     * * \f$g\f$ is the cell input and cell output activation function, usually
+     *   \f$tahn\f$,
+     * * \f$W_{xi}\f$ is the input-to-input weight matrix,
+     * * \f$W_{hi}\f$ is the recurrent to input weight matrix,
+     * * \f$W_{ci}\f$ is the cell-to-input weight matrix,
+     * * \f$b_i\f$ is the input gate bias,
+     * * \f$W_{xf}\f$ is the input-to-forget weight matrix,
+     * * \f$W_{hf}\f$ is the recurrent-to-forget weight matrix,
+     * * \f$W_{cf}\f$ is the cell-to-forget weight matrix,
+     * * \f$b_f\f$ is the forget gate bias,
+     * * \f$W_{xc}\f$ is the input-to-cell weight matrix,
+     * * \f$W_{hc}\f$ is the recurrent-to-cell weight matrix,
+     * * \f$b_c\f$ is the cell bias,
+     * * \f$W_{xo}\f$ is the input-to-output weight matrix,
+     * * \f$W_{ho}\f$ is the recurrent-to-output weight matrix,
+     * * \f$W_{co}\f$ is the cell-to-output weight matrix,
+     * * \f$b_o\f$ is the output gate bias,
+     * * \f$W_{proj}\f$ is the projection weight matrix,
+     * * \f$b_{proj}\f$ is the projection bias,
+     * * \f$t_{cell}\f$ is the threshold for clipping the cell state, and
+     * * \f$t_{proj}\f$ is the threshold for clipping the projected output.
+     * * \f$\odot\f$ is the
+     *   <a href="https://en.wikipedia.org/wiki/Hadamard_product_(matrices)">
+     *   Hadamard product</a> that takes two matrices and produces another
+     *   matrix, each element of which is the product of the corresponding
+     *   elements of the input matrices.
+     *
+     * Since HAL version 1.2 LSTM supports layer normalization.
+     * In case layer normalization is used, the inputs to internal activation
+     * functions (sigmoid and \f$g\f$) are normalized, rescaled and recentered
+     * following an approach from section 3.1 from
+     * https://arxiv.org/pdf/1607.06450.pdf
+     *
+     * The operation has the following independently optional inputs:
+     * * The cell-to-input weights (\f$W_{ci}\f$), cell-to-forget weights
+     *   (\f$W_{cf}\f$) and cell-to-output weights (\f$W_{co}\f$) either all
+     *   have values or neither of them have values (i.e., all set to null). If
+     *   they have values, the peephole optimization is used.
+     * * The input-to-input weights (\f$W_{xi}\f$), recurrent-to-input weights
+     *   (\f$W_{hi}\f$) and input gate bias (\f$b_i\f$) either all have values,
+     *   or none of them have values. If they have no values, coupling of input
+     *   and forget gates (CIFG) is used, in which case the input gate
+     *   (\f$i_t\f$) is calculated using the following equation instead.
+     *   \f{eqnarray*}{
+     *   i_t = 1 - f_t
+     *   \f}
+     *   In case peephole optimization is used and CIFG is not used
+     *   cell-to-input (\f$W_{ci}\f$) weights must be present. Otherwise, the
+     *   cell-to-input weights must have no value.
+     * * The projection weights (\f$W_{proj}\f$) is required only for the
+     *   recurrent projection layer, and should otherwise have no value.
+     * * The projection bias (\f$b_{proj}\f$) may (but not required to) have a
+     *   value if the recurrent projection layer exists, and should otherwise
+     *   have no value.
+     * * (HAL version 1.2 or later) The four layer normalization weights either all have
+     *   values or none of them have values. Additionally, if CIFG is used,
+     *   input layer normalization weights tensor is omitted and the other layer
+     *   normalization weights either all have values or none of them have
+     *   values. Layer normalization is used when the values of all the layer
+     *   normalization weights are present.
+     *
+     * References:
+     *
+     * The default non-peephole non-CIFG implementation is based on:
+     * http://www.bioinf.jku.at/publications/older/2604.pdf
+     * S. Hochreiter and J. Schmidhuber. "Long Short-Term Memory". Neural
+     * Computation, 9(8):1735-1780, 1997.
+     *
+     * The peephole implementation and projection layer is based on:
+     * https://research.google.com/pubs/archive/43905.pdf
+     * Hasim Sak, Andrew Senior, and Francoise Beaufays. "Long short-term memory
+     * recurrent neural network architectures for large scale acoustic
+     * modeling." INTERSPEECH, 2014.
+     * (However, the concept of peephole optimization was introduced in work
+     * prior to this paper.)
+     *
+     * The coupling of input and forget gate (CIFG) is based on:
+     * http://arxiv.org/pdf/1503.04069.pdf
+     * Greff et al. "LSTM: A Search Space Odyssey"
+     *
+     * The layer normalization is based on:
+     * https://arxiv.org/pdf/1607.06450.pdf
+     * Jimmy Ba et al. "Layer Normalization"
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * All input and output tensors must be of the same type.
+     *
+     * Inputs:
+     * * 0: The input (\f$x_t\f$).
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of cell units.
+     * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 4: The input-to-output weights (\f$W_{xo}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *      corresponds to either the number of cell units (i.e., “num_units”),
+     *      or the second dimension of the “projection_weights”, if defined.
+     * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 12:The input gate bias (\f$b_i\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 13:The forget gate bias (\f$b_f\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 14:The cell bias (\f$b_c\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 15:The output gate bias (\f$b_o\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+     *      A 2-D tensor of shape [output_size, num_units].
+     * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+     *      A 1-D tensor of shape [output_size].
+     * * 18:The output state (in) (\f$h_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 19:The cell state (in) (\f$C_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 20:The activation function (\f$g\f$).
+     *      A value indicating the activation function:
+     *      <ul>
+     *      <li>0: None;
+     *      <li>1: Relu;
+     *      <li>3: Relu6;
+     *      <li>4: Tanh;
+     *      <li>6: Sigmoid.
+     *      </ul>
+     * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+     *      that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *      then clipping is disabled.
+     *      Until HAL version 1.2 this scalar must be of type {@link
+     *      OperandType::FLOAT32}. Since HAL version 1.2, if all the input
+     *      tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+     *      scalar must be of the type {@link OperandType::FLOAT32},
+     *      otherwise if all the input tensors have the type {@link
+     *      OperandType::TENSOR_FLOAT16}, this scalar must be of type {@link
+     *      OperandType::FLOAT16}.
+     * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+     *      projection layer, such that values are bound within
+     *      [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     *      Until HAL version 1.2 this scalar must be of type {@link
+     *      OperandType::FLOAT32}. Since HAL version 1.2, if all the input
+     *      tensors have type {@link OperandType::TENSOR_FLOAT32}, this
+     *      scalar must be of the type {@link OperandType::FLOAT32},
+     *      otherwise if all the input tensors have the type {@link
+     *      OperandType::TENSOR_FLOAT16}, this scalar must be of type {@link
+     *      OperandType::FLOAT16}.
+     * Since HAL version 1.2 there are additional inputs to this op:
+     * * 23:The input layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at input gate.
+     * * 24:The forget layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at forget gate.
+     * * 25:The cell layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at cell gate.
+     * * 26:The output layer normalization weights.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at output gate.
+     *
+     * Outputs:
+     * * 0: The scratch buffer.
+     *      A 2-D tensor of shape [batch_size, num_units * 3] with CIFG, or
+     *      [batch_size, num_units * 4] without CIFG.
+     * * 1: The output state (out) (\f$h_t\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 2: The cell state (out) (\f$C_t\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 3: The output (\f$o_t\f$).
+     *      A 2-D tensor of shape [batch_size, output_size]. This is effectively
+     *      the same as the current “output state (out)” value.
+     */
+    LSTM = 16,
+    /**
+     * Performs an 2-D max pooling operation.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, channel] =
+     *         max_{di, dj} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj, channel]
+     *         )
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An optional {@link OperandType::BOOL} scalar, default to false.
+     *       Set to true to specify NCHW data layout for input0 and output0.
+     *       Available since HAL version 1.2.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the filter
+     *      width.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the filter
+     *      height.
+     * * 6: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 7: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    MAX_POOL_2D = 17,
+    /**
+     * Multiplies two tensors, element-wise.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the product of both input tensors, optionally
+     * modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the resulting output is the maximum size along each dimension
+     * of the input operands. It starts with the trailing dimensions, and works
+     * its way forward.
+     *
+     * Since HAL version 1.2, generic zero-sized input tensor is supported. Zero
+     * dimension is only compatible with 0 or 1. The size of the output
+     * dimension is zero if either of corresponding input dimension is zero.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
+     *
+     * Outputs:
+     * * 0: The product, a tensor of the same {@link OperandType} as input0.
+     *      For output tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the following condition must be satisfied:
+     *      output_scale > input1_scale * input2_scale.
+     */
+    MUL = 18,
+    /**
+     * Computes rectified linear activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = max(0, input)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RELU = 19,
+    /**
+     * Computes rectified linear 1 activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = min(1.f, max(-1.f, input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of the same shape as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RELU1 = 20,
+    /**
+     * Computes rectified linear 6 activation on the input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = min(6, max(0, input))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RELU6 = 21,
+    /**
+     * Reshapes a tensor.
+     *
+     * Given tensor, this operation returns a tensor that has the same values as
+     * tensor, but with a newly specified shape.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the tensor to be reshaped.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}, defining the
+     *      shape of the output tensor. The number of elements implied by shape
+     *      must be the same as the number of elements in the input tensor.
+     *
+     *      If one component of shape is the special value -1, the size of that
+     *      dimension is computed so that the total size remains constant. In
+     *      particular, a shape of [-1] flattens into 1-D. At most one component
+     *      of shape can be -1.
+     *
+     * Outputs:
+     * * 0: The output tensor, of shape specified by the input shape.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RESHAPE = 22,
+    /**
+     * Resizes images to given size using the bilinear interpretation.
+     *
+     * Resized images must be distorted if their output aspect ratio is not the
+     * same as input aspect ratio. The corner pixels of output may not be the
+     * same as corner pixels of input.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Both resizing by shape and resizing by scale are supported.
+     *
+     * Inputs (resizing by shape):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input.
+     *      Since HAL version 1.2, zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
+     *
+     * Inputs (resizing by scale, since HAL version 1.2):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input. Zero batches is supported for this tensor.
+     * * 1: A scalar, specifying width_scale, the scaling factor of the width
+     *      dimension from the input tensor to the output tensor. The output
+     *      width is calculated as new_width = floor(width * width_scale).
+     *      The scalar must be of {@link OperandType::FLOAT16} if input0 is
+     *      of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} otherwise.
+     * * 2: A scalar, specifying height_scale, the scaling factor of the height
+     *      dimension from the input tensor to the output tensor. The output
+     *      height is calculated as new_height = floor(height * height_scale).
+     *      The scalar must be of {@link OperandType::FLOAT16} if input0 is
+     *      of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} otherwise.
+     * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, new_height, new_width, depth].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RESIZE_BILINEAR = 23,
+    /**
+     * A basic recurrent neural network layer.
+     *
+     * This layer implements the operation:
+     * outputs = state = activation(inputs * input_weights +
+     *                              state * recurrent_weights + bias)
+     *
+     * Where:
+     * * “input_weights” is a weight matrix that multiplies the inputs;
+     * * “recurrent_weights” is a weight matrix that multiplies the current
+     *    “state” which itself is the output from the previous time step
+     *    computation;
+     * * “bias” is a bias vector (added to each output vector in the batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: weights.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of units.
+     * * 2: recurrent_weights.
+     *      A 2-D tensor of shape [num_units, num_units], with columns
+     *      corresponding to the weights from each unit.
+     * * 3: bias.
+     *      A 1-D tensor of shape [num_units].
+     * * 4: hidden state (in).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 5: fused_activation_function.
+     *      An optional {@link FusedActivationFunc} value indicating the
+     *      activation function. If “NONE” is specified then it results in a
+     *      linear activation.
+     *
+     * Outputs:
+     * * 0: hidden state (out).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     *
+     * * 1: output.
+     *      A 2-D tensor of shape [batch_size, num_units]. This is effectively
+     *      the same as the current state value.
+     */
+    RNN = 24,
+    /**
+     * Computes the softmax activation on the input tensor element-wise, per
+     * batch, by normalizing the input vector so the maximum coefficient is
+     * zero.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output[batch, i] =
+     *         exp((input[batch, i] - max(input[batch, :])) * beta) /
+     *         sum_{k}{exp((input[batch, k] - max(input[batch, :])) * beta)}
+     *
+     * For input tensor with rank other than 2, the activation will be applied
+     * independently on each 1-D slice along specified dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     * Tensors with rank other than 2 or 4 are only supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: A 2-D or 4-D tensor, specifying the tensor to be reshaped.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     * * 1: A scalar, specifying the positive scaling factor for the exponent,
+     *      beta. If input0 is of {@link OperandType::TENSOR_FLOAT32},
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, the scalar
+     *      must be of {@link OperandType::FLOAT32}.
+     *      If input0 is of {@link OperandType::TENSOR_FLOAT16}, then the
+     *      scalar must be of {@link OperandType::FLOAT16}.
+     * * 2: An optional {@link OperandType::INT32} scalar, default to -1,
+     *      specifying the dimension the activation would be performed on.
+     *      Negative index is used to specify axis from the end (e.g. -1 for
+     *      the last axis). Must be in the range [-n, n).
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 256 and the zeroPoint must be 0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the scale must be 1.f / 256 and the zeroPoint must be -128.
+     */
+    SOFTMAX = 25,
+    /**
+     * Rearranges blocks of spatial data, into depth.
+     *
+     * More specifically, this op outputs a copy of the input tensor where
+     * values from the height and width dimensions are moved to the depth
+     * dimension. The value block_size indicates the input block size and how
+     * the data is moved.
+     *
+     * Chunks of data of size block_size * block_size from depth are rearranged
+     * into non-overlapping blocks of size block_size x block_size.
+     *
+     * The depth of the output tensor is input_depth * block_size * block_size.
+     * The input tensor's height and width must be divisible by block_size.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the block_size.
+     *      block_size must be >=1 and block_size must be a divisor of both the
+     *      input height and width.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape [batches, height/block_size,
+     *      width/block_size, depth_in*block_size*block_size].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    SPACE_TO_DEPTH = 26,
+    /**
+     * SVDF op is a kind of stateful layer derived from the notion that a
+     * densely connected layer that's processing a sequence of input frames can
+     * be approximated by using a singular value decomposition of each of its
+     * nodes. The implementation is based on:
+     *
+     * https://research.google.com/pubs/archive/43813.pdf
+     *
+     * P. Nakkiran, R. Alvarez, R. Prabhavalkar, C. Parada.
+     * “Compressing Deep Neural Networks using a Rank-Constrained Topology”.
+     * INTERSPEECH, 2015.
+     *
+     * It processes the incoming input using a 2-stage filtering mechanism:
+     * * stage 1 performs filtering on the "features" dimension, whose outputs
+     *   get pushed into a memory of fixed-size memory_size.
+     * * stage 2 performs filtering on the "time" dimension of the memory_size
+     *   memoized outputs of stage 1.
+     *
+     * Specifically, for rank 1, this layer implements the operation:
+     *
+     *     memory = push(conv1d(inputs, weights_feature, feature_dim,
+     *                          "PADDING_VALID"));
+     *     outputs = activation(memory * weights_time + bias);
+     *
+     * Where:
+     * * “weights_feature” is a weights matrix that processes the inputs (by
+     *   convolving the input with every “feature filter”), and whose outputs
+     *   get pushed, stacked in order, into the fixed-size “memory” (the oldest
+     *   entry gets dropped);
+     * * “weights_time” is a weights matrix that processes the “memory” (by a
+     *   batched matrix multiplication on the num_units);
+     * * “bias” is an optional bias vector (added to each output vector in the
+     *   batch); and
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Each rank adds a dimension to the weights matrices by means of stacking
+     * the filters.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * All input tensors must be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 2-D tensor of shape [batch_size, input_size], where “batch_size”
+     *      corresponds to the batching dimension, and “input_size” is the size
+     *      of the input.
+     * * 1: weights_feature.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of units.
+     * * 2: weights_time.
+     *      A 2-D tensor of shape [num_units, memory_size], where “memory_size”
+     *      corresponds to the fixed-size of the memory.
+     * * 3: bias.
+     *      An optional 1-D tensor of shape [num_units].
+     * * 4: state (in).
+     *      A 2-D tensor of shape [batch_size, (memory_size - 1) * num_units * rank].
+     * * 5: rank.
+     *      The rank of the SVD approximation.
+     * * 6: fused_activation_function.
+     *      An optional {@link FusedActivationFunc} value indicating the
+     *      activation function. If “NONE” is specified then it results in a
+     *      linear activation.
+     *
+     * Outputs:
+     * * 0: state (out).
+     *      A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+     *      [batch_size, (memory_size - 1) * num_units * rank].
+     * * 1: output.
+     *      A 2-D tensor of the same {@link OperandType} as the inputs, with shape
+     *      [batch_size, num_units].
+     */
+    SVDF = 27,
+    /**
+     * Computes hyperbolic tangent of input tensor element-wise.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = tanh(input)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      the scale must be 1.f / 128 and the zeroPoint must be 128.
+     *      For {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the scale must be 1.f / 128 and the zeroPoint must be 0.
+     */
+    TANH = 28,
+    /**
+     * BatchToSpace for N-dimensional tensors.
+     *
+     * This operation reshapes the batch dimension (dimension 0) into M + 1
+     * dimensions of shape block_shape + [batch], interleaves these blocks back
+     * into the grid defined by the spatial dimensions [1, ..., M], to obtain a
+     * result with the same rank as the input.
+     *
+     * This is the reverse of SpaceToBatch.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be reshaped
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+     *      sizes for each spatial dimension of the input tensor. All values
+     *      must be >= 1.
+     * * 2: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since API level 29.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    BATCH_TO_SPACE_ND = 29,
+    /**
+     * Element-wise division of two tensors.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the result of dividing the first input tensor
+     * by the second, optionally modified by an activation function.
+     *
+     * For inputs of {@link OperandType::TENSOR_INT32}, performs
+     * "floor division" ("//" in Python). For example,
+     *     5 // 2 = 2
+     *    -5 // 2 = -3
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input1.dimension =    {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Since HAL version 1.2, generic zero-sized input tensor is supported. Zero
+     * dimension is only compatible with 0 or 1. The size of the output
+     * dimension is zero if either of corresponding input dimension is zero.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the first input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     */
+    DIV = 30,
+    /**
+     * Computes the mean of elements across dimensions of a tensor.
+     *
+     * Reduces the input tensor along the given dimensions to reduce. Unless
+     * keep_dims is true, the rank of the tensor is reduced by 1 for each entry
+     * in axis. If keep_dims is true, the reduced dimensions are retained with
+     * length 1.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Must be in the range
+     *      [-rank(input_tensor), rank(input_tensor)).
+     *
+     *      NOTE: When the operation was introduced, the documentation
+     *      incorrectly stated that if dimensions were empty, the operation
+     *      would reduce across all dimensions. This behavior was never
+     *      implemented.
+     *
+     * * 2: An {@link OperandType::INT32} scalar, keep_dims. If positive,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     */
+    MEAN = 31,
+    /**
+     * Pads a tensor.
+     *
+     * This operation pads a tensor according to the specified paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *   (full support since HAL version 1.2, see the output section)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be padded.
+     * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. The shape of the
+     *      tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of elements to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of elements to be padded after the
+     *      end of dimension i.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The
+     *      output tensor has the same rank as input0, and each
+     *      dimension of the output tensor has the same size as the
+     *      corresponding dimension of the input tensor plus the size
+     *      of the padding:
+     *          output0.dimension[i] =
+     *              padding[i, 0] + input0.dimension[i] + padding[i, 1]
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     *
+     *      NOTE: Before HAL version 1.2, the pad value for
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} is undefined.
+     *      Since HAL version 1.2, the pad value is always the logical zero.
+     */
+    PAD = 32,
+    /**
+     * SpaceToBatch for N-Dimensional tensors.
+     *
+     * This operation divides "spatial" dimensions [1, ..., M] of the input into
+     * a grid of blocks of shape block_shape, and interleaves these blocks with
+     * the "batch" dimension (0) such that in the output, the spatial dimensions
+     * [1, ..., M] correspond to the position within the grid, and the batch
+     * dimension combines both the position within a spatial block and the
+     * original batch position. Prior to division into blocks, the spatial
+     * dimensions of the input are optionally zero padded according to paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *   (full support since HAL version 1.2, see the output section)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     * NCHW is supported since HAL version 1.2.
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the input.
+     * * 1: A 1-D Tensor of {@link OperandType::TENSOR_INT32}, the block
+     *      sizes for each spatial dimension of the input tensor. All values
+     *      must be >= 1.
+     * * 2: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. All values must be
+     *      >= 0. The shape of the tensor must be {M, 2}, where M is the number
+     *      of spatial dimensions.
+     *      padding[i, 0] specifies the number of element to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of element to be padded after the
+     *      end of dimension i.
+     * * 3: An optional {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     *      Available since HAL version 1.2.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     *
+     *      NOTE: Before HAL version 1.2, the pad value for
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} is undefined.
+     *      Since HAL version 1.2, the pad value is always the logical zero.
+     */
+    SPACE_TO_BATCH_ND = 33,
+    /**
+     * Removes dimensions of size 1 from the shape of a tensor.
+     *
+     * Given a tensor input, this operation returns a tensor of the same
+     * {@link OperandType} with all dimensions of size 1 removed. If you don't
+     * want to remove all size 1 dimensions, you can remove specific size 1
+     * dimensions by specifying the axes (input1).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, the tensor to be squeezed.
+     * * 1: An optional 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      dimensions to squeeze. If specified only squeezes the dimensions
+     *      listed. Otherwise, squeezes all dimensions. The dimension index
+     *      starts at 0. An error must be reported if squeezing a dimension that
+     *      is not 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. Contains the
+     *      same data as input, but has one or more dimensions of size 1
+     *      removed.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     *      If all input dimensions are equal to 1 and are to be squeezed, the
+     *      output shape is [1].
+     */
+    SQUEEZE = 34,
+    /**
+     * Extracts a strided slice of a tensor.
+     *
+     * Roughly speaking, this op extracts a slice of size (end - begin) / stride
+     * from the given input tensor. Starting at the location specified by begin
+     * the slice continues by adding stride to the index until all dimensions
+     * are not less than end. Note that a stride can be negative, which causes a
+     * reverse slice.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be sliced.
+     * * 1: begin, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      starts of the dimensions of the input tensor to be sliced. The
+     *      length must be of rank(input0).
+     * * 2: end, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      ends of the dimensions of the input tensor to be sliced. The length
+     *      must be of rank(input0).
+     * * 3: strides, a 1-D tensor of {@link OperandType::TENSOR_INT32}. The
+     *      strides of the dimensions of the input tensor to be sliced. The
+     *      length must be of rank(input0). The entries must be non-zero.
+     * * 4: begin_mask, an {@link OperandType::INT32} scalar. If the ith bit
+     *      of begin_mask is set, begin[i] is ignored and the fullest possible
+     *      range in that dimension is used instead.
+     * * 5: end_mask, an {@link OperandType::INT32} scalar. If the ith bit of
+     *      end_mask is set, end[i] is ignored and the fullest possible range in
+     *      that dimension is used instead.
+     * * 6: shrink_axis_mask, an {@link OperandType::INT32} scalar. If the
+     *      ith bit of shrink_axis_mask is set, the ith dimension specification
+     *      shrinks the dimensionality by 1, taking on the value at index
+     *      begin[i]. In this case, the ith specification must define a
+     *      slice of size 1, e.g. begin[i] = x, end[i] = x + 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0 and rank (n - k),
+     *      where k is the number of bits set in shrink_axis_mask.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     *      If shrink_axis_mask is true for all input dimensions, the output
+     *      shape is [1].
+     */
+    STRIDED_SLICE = 35,
+    /**
+     * Element-wise subtraction of two tensors.
+     *
+     * Takes two input tensors of identical {@link OperandType} and compatible
+     * dimensions. The output is the result of subtracting the second input
+     * tensor from the first one, optionally modified by an activation function.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input1.dimension =    {4, 1, 2}
+     *     input2.dimension = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Since HAL version 1.2, generic zero-sized input tensor is supported. Zero
+     * dimension is only compatible with 0 or 1. The size of the output
+     * dimension is zero if either of corresponding input dimension is zero.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the first input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0.
+     * * 2: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     *      For a {@link OperandType::TENSOR_INT32} tensor,
+     *      the {@link FusedActivationFunc} must be "NONE".
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    SUB = 36,
+    /**
+     * Transposes the input tensor, permuting the dimensions according to the
+     * perm tensor.
+     *
+     * The returned tensor's dimension i corresponds to the input dimension
+     * perm[i]. If perm is not given, it is set to (n-1...0), where n is the
+     * rank of the input tensor. Hence by default, this operation performs a
+     * regular matrix transpose on 2-D input Tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16} (since HAL version 1.2)
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be transposed.
+     *      Since HAL version 1.2, this tensor may be zero-sized.
+     * * 1: An optional 1-D Tensor of {@link OperandType::TENSOR_INT32},
+     *      the permutation of the dimensions of the input tensor.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    TRANSPOSE = 37,
+    /**
+     * Computes the absolute value of a tensor, element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    ABS = 38,
+    /**
+     * Returns the index of the largest element along an axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor specifying the input. Must be non-empty.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+     *      If input is 1-dimensional, the output shape is [1].
+     */
+    ARGMAX = 39,
+    /**
+     * Returns the index of the smallest element along an axis.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor specifying the input. Must be non-empty.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: An (n - 1)-D {@link OperandType::TENSOR_INT32} tensor.
+     *      If input is 1-dimensional, the output shape is [1].
+     */
+    ARGMIN = 40,
+    /**
+     * Transform axis-aligned bounding box proposals using bounding box deltas.
+     *
+     * Given the positions of bounding box proposals and the corresponding
+     * bounding box deltas for each class, return the refined bounding box
+     * regions. The resulting bounding boxes are cliped against the edges of
+     * the image.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT16_ASYMM}
+     *
+     * Inputs:
+     * * 0: A 2-D Tensor of shape [num_rois, 4], specifying the locations of the
+     *      bounding box proposals, each line with format [x1, y1, x2, y2].
+     *      For tensor of type {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      the zeroPoint must be 0 and the scale must be 0.125. Zero num_rois
+     *      is supported for this tensor.
+     * * 1: A 2-D Tensor of shape [num_rois, num_classes * 4], specifying the
+     *      bounding box delta for each region of interest and each class. The
+     *      bounding box deltas are organized in the following order
+     *      [dx, dy, dw, dh], where dx and dy is the relative correction factor
+     *      for the center position of the bounding box with respect to the width
+     *      and height, dw and dh is the log-scale relative correction factor
+     *      for the width and height. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, this tensor should be
+     *      of {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}. Zero num_rois is
+     *      supported for this tensor.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together. Zero num_rois is
+     *      supported for this tensor.
+     * * 3: A 2-D Tensor of shape [batches, 2], specifying the information of
+     *      each image in the batch, each line with format
+     *      [image_height, image_width].
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, with shape
+     *      [num_rois, num_classes * 4], specifying the coordinates of each
+     *      output bounding box for each class, with format [x1, y1, x2, y2].
+     *      For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
+     *      scale must be 0.125 and the zero point must be 0.
+     */
+    AXIS_ALIGNED_BBOX_TRANSFORM = 41,
+    /**
+     * A recurrent neural network layer that applies an LSTM cell to a
+     * sequence of inputs in forward and backward directions.
+     *
+     * The op supports cross-linking via an auxiliary input. Regular cell feeds
+     * one input into the two RNN cells in the following way:
+     *
+     *       INPUT  (INPUT_REVERSED)
+     *         |         |
+     *    ---------------------
+     *    | FW_LSTM   BW_LSTM |
+     *    ---------------------
+     *         |         |
+     *      FW_OUT     BW_OUT
+     *
+     * An op with cross-linking takes two inputs and feeds them into the RNN
+     * cells in the following way:
+     *
+     *       AUX_INPUT   (AUX_INPUT_REVERSED)
+     *           |             |
+     *     INPUT | (INPUT_R'D.)|
+     *       |   |       |     |
+     *    -----------------------
+     *    |  \  /        \    / |
+     *    | FW_LSTM     BW_LSTM |
+     *    -----------------------
+     *         |           |
+     *      FW_OUT      BW_OUT
+     *
+     * The cross-linking mode is enabled iff auxiliary input and auxiliary
+     * weights are present. While stacking this op on top of itself, this
+     * allows to connect both forward and backward outputs from previous cell
+     * to the next cell's input.
+     *
+     * Since HAL version 1.3 parallel linking mode is supported. The mode is
+     * enabled if auxiliary input is present but auxiliary weights are omitted.
+     * In this case, the cell feeds inputs into the RNN in the following way:
+     *
+     *       INPUT (AUX_INPUT_REVERSED)
+     *         |         |
+     *    ---------------------
+     *    | FW_LSTM   BW_LSTM |
+     *    ---------------------
+     *         |         |
+     *      FW_OUT     BW_OUT
+     *
+     * While stacking this op on top of itself, this allows to connect both
+     * forward and backward outputs from previous cell to the next cell's
+     * corresponding inputs.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 3, either time-major or batch-major.
+     *
+     * All input and output tensors must be of the same type.
+     *
+     * Inputs:
+     * * 0: The input.
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, input_size]
+     *        If batch-major: [batch_size, max_time, input_size]
+     *      where "max_time" is the number of timesteps (sequence length),
+     *      "batch_size" corresponds to the batching dimension, and
+     *      "input_size" is the size of the input.
+     * * 1: The forward input-to-input weights. Optional.
+     *      A 2-D tensor of shape [fw_num_units, input_size], where “fw_num_units”
+     *      corresponds to the number of forward cell units.
+     * * 2: The forward input-to-forget weights.
+     *      A 2-D tensor of shape [fw_num_units, input_size].
+     * * 3: The forward input-to-cell weights.
+     *      A 2-D tensor of shape [fw_num_units, input_size].
+     * * 4: The forward input-to-output weights.
+     *      A 2-D tensor of shape [fw_num_units, input_size].
+     * * 5: The forward recurrent-to-input weights. Optional.
+     *      A 2-D tensor of shape [fw_num_units, fw_output_size], where “fw_output_size”
+     *      corresponds to either the number of cell units (i.e., fw_num_units),
+     *      or the second dimension of the “fw_projection_weights”, if defined.
+     * * 6: The forward recurrent-to-forget weights.
+     *      A 2-D tensor of shape [fw_num_units, fw_output_size].
+     * * 7: The forward recurrent-to-cell weights.
+     *      A 2-D tensor of shape [fw_num_units, fw_output_size].
+     * * 8: The forward recurrent-to-output weights.
+     *      A 2-D tensor of shape [fw_num_units, fw_output_size].
+     * * 9: The forward cell-to-input weights. Optional.
+     *      A 1-D tensor of shape [fw_num_units].
+     * * 10: The forward cell-to-forget weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 11: The forward cell-to-output weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 12: The forward input gate bias. Optional.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 13: The forward forget gate bias.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 14: The forward cell gate bias.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 15: The forward output gate bias.
+     *       A 1-D tensor of shape [fw_num_units].
+     * * 16: The forward projection weights. Optional.
+     *       A 2-D tensor of shape [fw_output_size, fw_num_units].
+     * * 17: The forward projection bias. Optional.
+     *       A 1-D tensor of shape [fw_output_size].
+     * * 18: The backward input-to-input weights. Optional.
+     *       A 2-D tensor of shape [bw_num_units, input_size], where “bw_num_units”
+     *       corresponds to the number of backward cell units.
+     * * 19: The backward input-to-forget weights.
+     *       A 2-D tensor of shape [bw_num_units, input_size].
+     * * 20: The backward input-to-cell weights.
+     *       A 2-D tensor of shape [bw_num_units, input_size].
+     * * 21: The backward input-to-output weights.
+     *       A 2-D tensor of shape [bw_num_units, input_size].
+     * * 22: The backward recurrent-to-input weights. Optional.
+     *       A 2-D tensor of shape [bw_num_units, bw_output_size], where “bw_output_size”
+     *       corresponds to either the number of cell units (i.e., “bw_num_units”),
+     *       or the second dimension of the “bw_projection_weights”, if defined.
+     * * 23: The backward recurrent-to-forget weights.
+     *       A 2-D tensor of shape [bw_num_units, bw_output_size].
+     * * 24: The backward recurrent-to-cell weights.
+     *       A 2-D tensor of shape [bw_num_units, bw_output_size].
+     * * 25: The backward recurrent-to-output weights.
+     *       A 2-D tensor of shape [bw_num_units, bw_output_size].
+     * * 26: The backward cell-to-input weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 27: The backward cell-to-forget weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 28: The backward cell-to-output weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 29: The backward input gate bias. Optional.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 30: The backward forget gate bias.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 31: The backward cell gate bias.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 32: The backward output gate bias.
+     *       A 1-D tensor of shape [bw_num_units].
+     * * 33: The backward projection weights. Optional.
+     *       A 2-D tensor of shape [bw_output_size, bw_num_units].
+     * * 34: The backward projection bias. Optional.
+     *       A 1-D tensor of shape [bw_output_size].
+     * * 35: The forward input activation state.
+     *       A 2-D tensor of shape [batch_size, bw_output_size].
+     * * 36: The forward input cell state.
+     *       A 2-D tensor of shape [batch_size, bw_num_units].
+     * * 37: The backward input activation state.
+     *       A 2-D tensor of shape [batch_size, bw_output_size].
+     * * 38: The backward input cell state.
+     *       A 2-D tensor of shape [batch_size, bw_num_units].
+     * * 39: The auxiliary input. Optional.
+     *       A 3-D tensor of shape [max_time, batch_size, aux_input_size],
+     *       where “batch_size” corresponds to the batching dimension, and
+     *       “aux_input_size” is the size of the auxiliary input. Optional. See
+     *       the docs above for the usage modes explanation.
+     * * 40: The forward auxiliary input-to-input weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [fw_num_units, aux_input_size].
+     * * 41: The forward auxiliary input-to-forget weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [fw_num_units, aux_input_size].
+     * * 42: The forward auxiliary input-to-cell weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [fw_num_units, aux_input_size].
+     * * 43: The forward auxiliary input-to-output weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [fw_num_units, aux_input_size].
+     * * 44: The backward auxiliary input-to-input weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [bw_num_units, aux_input_size].
+     * * 45: The backward auxiliary input-to-forget weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [bw_num_units, aux_input_size].
+     * * 46: The backward auxiliary input-to-cell weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [bw_num_units, aux_input_size].
+     * * 47: The backward auxiliary input-to-output weights.
+     *       Optional. See the docs above for the usage modes explanation.
+     *       A 2-D tensor of shape [bw_num_units, aux_input_size].
+     * * 48: The activation function.
+     *       A value indicating the activation function:
+     *       <ul>
+     *       <li>0: None;
+     *       <li>1: Relu;
+     *       <li>3: Relu6;
+     *       <li>4: Tanh;
+     *       <li>6: Sigmoid.
+     *       </ul>
+     * * 49: The clipping threshold for the cell state, such
+     *       that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *       then clipping is disabled.
+     *       If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+     *       this scalar must be of the type {@link OperandType::FLOAT32},
+     *       otherwise if all the input tensors have the type
+     *       {@link OperandType::TENSOR_FLOAT16}, this scalar must be
+     *       of type {@link OperandType::FLOAT16}.
+     * * 50: The clipping threshold for the output from the
+     *       projection layer, such that values are bound within
+     *       [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     *       If all the input tensors have type {@link OperandType::TENSOR_FLOAT32},
+     *       this scalar must be of the type {@link OperandType::FLOAT32},
+     *       otherwise if all the input tensors have the type
+     *       {@link OperandType::TENSOR_FLOAT16}, this scalar must be
+     *       of type {@link OperandType::FLOAT16}.
+     * * 51: merge_outputs
+     *       An {@link OperandType::BOOL} scalar specifying if the outputs
+     *       from forward and backward cells should be merged.
+     * * 52: time_major
+     *       An {@link OperandType::BOOL} scalar specifying the shape format
+     *       of input and output tensors.
+     * * 53: The forward input layer normalization weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+     *       to activation at input gate.
+     * * 54: The forward forget layer normalization weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+     *       to activation at forget gate.
+     * * 55: The forward cell layer normalization weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+     *       to activation at cell gate.
+     * * 56: The forward output layer normalization weights. Optional.
+     *       A 1-D tensor of shape [fw_num_units]. Used to rescale normalized inputs
+     *       to activation at output gate.
+     * * 57: The backward input layer normalization weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+     *       to activation at input gate.
+     * * 58: The backward forget layer normalization weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+     *       to activation at forget gate.
+     * * 59: The backward cell layer normalization weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+     *       to activation at cell gate.
+     * * 60: The backward output layer normalization weights. Optional.
+     *       A 1-D tensor of shape [bw_num_units]. Used to rescale normalized inputs
+     *       to activation at output gate.
+     *
+     * Outputs:
+     * * 0: The forward output.
+     *      A 3-D tensor of shape:
+     *        If time-major and not merge_outputs:
+     *          [max_time, batch_size, fw_output_size]
+     *        If time-major and merge_outputs:
+     *          [max_time, batch_size, fw_output_size + bw_output_size]
+     *        If batch-major and not merge_outputs:
+     *          [batch_size, max_time, fw_output_size]
+     *        If batch-major and merge_outputs:
+     *          [batch_size, max_time, fw_output_size + bw_output_size]
+     * * 1: The backward output.  Unused if merge_outputs is true.
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, bw_output_size]
+     *        If batch-major: [batch_size, max_time, bw_output_size]
+     * * 2: The forward activation state output.
+     *      A 2-D tensor of shape [batch_size, fw_output_size] containing an
+     *      activation state from the last time step in the sequence. This
+     *      output is optional and can be omitted. If this output is present
+     *      then outputs 3-5 must be present as well.
+     *      Available since HAL version 1.3.
+     * * 3: The forward cell state output.
+     *      A tensor of shape [batch_size, fw_cell_size] containing a cell state
+     *      from the last time step in the sequence. This output is optional
+     *      and can be omitted. If this output is present
+     *      then outputs 2, 4, 5 must be present as well.
+     *      Available since HAL version 1.3.
+     * * 4: The backward activation state output.
+     *      A 2-D tensor of shape [batch_size, bw_output_size] containing an
+     *      activation state from the last time step in the sequence. This
+     *      output is optional and can be omitted. If this output is present
+     *      then outputs 2, 3, 5 must be present as well.
+     *      Available since HAL version 1.3.
+     * * 5: The backward cell state output.
+     *      A tensor of shape [batch_size, bw_cell_size] containing a cell state
+     *      from the last time step in the sequence. This output is optional
+     *      and can be omitted. If this output is present
+     *      then outputs 2-4 must be present as well.
+     *      Available since HAL version 1.3.
+     */
+    BIDIRECTIONAL_SEQUENCE_LSTM = 42,
+    /**
+     * A recurrent neural network layer that applies a basic RNN cell to a
+     * sequence of inputs in forward and backward directions.
+     *
+     * This Op unrolls the input along the sequence dimension, and implements
+     * the following operation for each element in the sequence s =
+     * 1...sequence_length:
+     *   fw_outputs[s] = fw_state = activation(inputs[s] * fw_input_weights’ +
+     *          fw_state * fw_recurrent_weights’ + fw_bias)
+     *
+     * And for each element in sequence t = sequence_length : 1
+     *   bw_outputs[t] = bw_state = activation(inputs[t] * bw_input_weights’ +
+     *          bw_state * bw_recurrent_weights’ + bw_bias)
+     *
+     * Where:
+     * * “{fw,bw}_input_weights” is a weight matrix that multiplies the inputs;
+     * * “{fw,bw}_recurrent_weights” is a weight matrix that multiplies the
+     *    current “state” which itself is the output from the previous time step
+     *    computation;
+     * * “{fw,bw}_bias” is a bias vector (added to each output vector in the
+     *    batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * The op supports cross-linking via an auxiliary input. Regular cell feeds
+     * one input into the two RNN cells in the following way:
+     *
+     *       INPUT  (INPUT_REVERSED)
+     *         |         |
+     *    ---------------------
+     *    | FW_RNN     BW_RNN |
+     *    ---------------------
+     *         |         |
+     *      FW_OUT     BW_OUT
+     *
+     * An op with cross-linking takes two inputs and feeds them into the RNN
+     * cells in the following way:
+     *
+     *       AUX_INPUT   (AUX_INPUT_REVERSED)
+     *           |             |
+     *     INPUT | (INPUT_R'D.)|
+     *       |   |       |     |
+     *    -----------------------
+     *    |  \  /        \    / |
+     *    | FW_RNN       BW_RNN |
+     *    -----------------------
+     *         |           |
+     *      FW_OUT      BW_OUT
+     *
+     * The cross-linking mode is enabled iff auxiliary input and auxiliary
+     * weights are present. While stacking this op on top of itself, this
+     * allows to connect both forward and backward outputs from previous cell
+     * to the next cell's input.
+     *
+     * Since HAL version 1.3 parallel linking mode is supported. The mode is
+     * enabled if auxiliary input is present but auxiliary weights are omitted.
+     * In this case, the cell feeds inputs into the RNN in the following way:
+     *
+     *       INPUT (AUX_INPUT_REVERSED)
+     *         |         |
+     *    ---------------------
+     *    | FW_RNN     BW_RNN |
+     *    ---------------------
+     *         |         |
+     *      FW_OUT     BW_OUT
+     *
+     * While stacking this op on top of itself, this allows to connect both
+     * forward and backward outputs from previous cell to the next cell's
+     * corresponding inputs.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to true, then the input has a shape [maxTime, batchSize,
+     *      inputSize], otherwise the input has a shape [batchSize, maxTime,
+     *      inputSize].
+     * * 1: fwWeights.
+     *      A 2-D tensor of shape [fwNumUnits, inputSize].
+     * * 2: fwRecurrentWeights.
+     *      A 2-D tensor of shape [fwNumUnits, fwNumUnits].
+     * * 3: fwBias.
+     *      A 1-D tensor of shape [fwNumUnits].
+     * * 4: fwHiddenState.
+     *      A 2-D tensor of shape [batchSize, fwNumUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 5: bwWeights.
+     *      A 2-D tensor of shape [bwNumUnits, inputSize].
+     * * 6: bwRecurrentWeights.
+     *      A 2-D tensor of shape [bwNumUnits, bwNumUnits].
+     * * 7: bwBias.
+     *      A 1-D tensor of shape [bwNumUnits].
+     * * 8: bwHiddenState
+     *      A 2-D tensor of shape [batchSize, bwNumUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 9: auxInput.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to true, then the input has a shape [maxTime, batchSize,
+     *      auxInputSize], otherwise the input has a shape [batchSize, maxTime,
+     *      auxInputSize]. Can be omitted. See the docs above for the usage
+     *      modes explanation.
+     * * 10:fwAuxWeights.
+     *      A 2-D tensor of shape [fwNumUnits, auxInputSize]. Can be omitted.
+     *      See the docs above for the usage modes explanation.
+     * * 11:bwAuxWeights.
+     *      A 2-D tensor of shape [bwNumUnits, auxInputSize]. Can be omitted.
+     *      See the docs above for the usage modes explanation.
+     * * 12:fusedActivationFunction.
+     *      A {@link FusedActivationFunc} value indicating the activation function. If
+     *      “NONE” is specified then it results in a linear activation.
+     * * 13:timeMajor
+     *      An {@link OperandType::BOOL} scalar specifying the shape format
+     *      of input and output tensors.
+     * * 14:mergeOutputs
+     *      An {@link OperandType::BOOL} scalar specifying if the outputs
+     *      from forward and backward cells are separate (if set to false) or
+     *      concatenated (if set to true).
+     * Outputs:
+     * * 0: fwOutput.
+     *      A 3-D tensor. The first two dimensions of the shape are defined by
+     *      the input 6 (timeMajor) and the third dimension is defined by the
+     *      input 14 (mergeOutputs). If timeMajor is set to true, then the first
+     *      two dimensions are [maxTime, batchSize], otherwise they are set to
+     *      [batchSize, maxTime]. If mergeOutputs is set to true, then the third
+     *      dimension is equal to (fwNumUnits + bwNumUnits), otherwise it is set
+     *      to fwNumUnits.
+     * * 1: bwOutput.
+     *      A 3-D tensor. If the input 14 (mergeOutputs) is set to true, then
+     *      this tensor is not produced. The shape is defined by the input 6
+     *      (timeMajor). If it is set to true, then the shape is set to
+     *      [maxTime, batchSize, bwNumUnits], otherwise the shape is set to
+     *      [batchSize, maxTime, bwNumUnits].
+     * * 2: The forward hidden state output.
+     *      A 2-D tensor of shape [batchSize, fwNumUnits] containing a hidden
+     *      state from the last time step in the sequence. This output is
+     *      optional and can be omitted. If this output is present then output
+     *      3 must be present as well.
+     *      Available since HAL version 1.3.
+     * * 3: The backward hidden state output.
+     *      A 2-D tensor of shape [batchSize, bwNumUnits] containing a hidden
+     *      state from the last time step in the sequence. This output is
+     *      optional and can be omitted. If this output is present then output
+     *      2 must be present as well.
+     *      Available since HAL version 1.3.
+     */
+    BIDIRECTIONAL_SEQUENCE_RNN = 43,
+    /**
+     * Greedily selects a subset of bounding boxes in descending order of score.
+     *
+     * This op applies NMS algorithm to each class. In each loop of execution,
+     * the box with maximum score gets selected and removed from the pending set.
+     * The scores of the rest of boxes are lowered according to the
+     * intersection-over-union (IOU) overlapping with the previously selected
+     * boxes and a specified NMS kernel method. Any boxes with score less
+     * than a threshold are removed from the pending set.
+     *
+     * Three NMS kernels are supported:
+     * * Hard:     score_new = score_old * (1 if IoU < threshold else 0)
+     * * Linear:   score_new = score_old * (1 if IoU < threshold else 1 - IoU)
+     * * Gaussian: score_new = score_old * exp(- IoU^2 / sigma)
+     *
+     * Axis-aligned bounding boxes are represented by its upper-left corner
+     * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+     * bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Inputs:
+     * * 0: A 2-D Tensor of shape [num_rois, num_classes], specifying the score
+     *      of each bounding box proposal. The boxes are grouped by batches in the
+     *      first dimension. Zero num_rois is supported for this tensor.
+     * * 1: A 2-D Tensor specifying the bounding boxes of shape
+     *      [num_rois, num_classes * 4], organized in the order [x1, y1, x2, y2].
+     *      The boxes are grouped by batches in the first dimension. The sequential
+     *      order of the boxes corresponds with input0. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should be of
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint of 0 and
+     *      scale of 0.125.
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      with zeroPoint of -128 and scale of 0.125.
+     *      Zero num_rois is supported for this tensor.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: An {@link OperandType::FLOAT32} scalar, score_threshold. Boxes
+     *      with scores lower than the threshold are filtered before sending
+     *      to the NMS algorithm.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of selected bounding boxes for each image. Set to a negative
+     *      value for unlimited number of output bounding boxes.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the NMS
+     *      kernel method, options are 0:hard, 1:linear, 2:gaussian.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+     *      threshold in hard and linear NMS kernel. This field is ignored if
+     *      gaussian kernel is selected.
+     * * 7: An {@link OperandType::FLOAT32} scalar, specifying the sigma in
+     *      gaussian NMS kernel. This field is ignored if gaussian kernel is
+     *      not selected.
+     * * 8: An {@link OperandType::FLOAT32} scalar, nms_score_threshold.
+     *      Boxes with scores lower than the threshold are dropped during the
+     *      score updating phase in soft NMS.
+     *
+     * Outputs:
+     * * 0: A 1-D Tensor of the same {@link OperandType} as input0, with shape
+     *      [num_output_rois], specifying the score of each output box. The boxes
+     *      are grouped by batches, but the sequential order in each batch is not
+     *      guaranteed. For type of {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      guaranteed. For type of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      or {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the scale and zero point must be the same as input0.
+     * * 1: A 2-D Tensor of the same {@link OperandType} as input1, with shape
+     *      [num_output_rois, 4], specifying the coordinates of each
+     *      output bounding box with the same format as input1. The sequential
+     *      order of the boxes corresponds with output0. For type of
+     *      {@link OperandType::TENSOR_QUANT16_ASYMM}, the scale must be
+     *      0.125 and the zero point must be 0.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_output_rois], specifying the class of each output box. The
+     *      sequential order of the boxes corresponds with output0.
+     * * 3: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_output_rois], specifying the batch index of each box. Boxes
+     *      with the same batch index are grouped together.
+     */
+    BOX_WITH_NMS_LIMIT = 44,
+    /**
+     * Casts a tensor to a type.
+     *
+     * This operation ignores the scale and zeroPoint of quanized tensors,
+     * e.g. it treats a {@link OperandType::TENSOR_QUANT8_ASYMM} input
+     * as a tensor of uint8 values.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * Since HAL version 1.3, casting tensors of the following
+     * {@link OperandType} to the same {@link OperandType} is supported:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT16_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT16_SYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: A tensor with the same shape as input0.
+     */
+    CAST = 45,
+    /**
+     * Shuffle the channels of the input tensor.
+     *
+     * Given an input tensor and a integer value of num_groups, CHANNEL_SHUFFLE
+     * divide the channel dimension into num_groups groups, and reorganize the
+     * channels by grouping channels with the same index in each group.
+     *
+     * Along the channel dimension, the output is calculated using this formula:
+     *
+     *     output_channel[k * num_groups + g] = input_channel[g * group_size + k]
+     *
+     * where group_size = num_channels / num_groups
+     *
+     * The number of channels must be divisible by num_groups.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be shuffled.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the number of
+     *      groups.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the dimension
+     *      channel shuffle would be performed on. Negative index is used to
+     *      specify axis from the end (e.g. -1 for the last axis). Must be in
+     *      the range [-n, n).
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    CHANNEL_SHUFFLE = 46,
+    /**
+     * Apply postprocessing steps to bounding box detections.
+     *
+     * Bounding box detections are generated by applying transformation on a set
+     * of predefined anchors with the bounding box deltas from bounding box
+     * regression. A final step of hard NMS is applied to limit the number of
+     * returned boxes.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Inputs:
+     * * 0: A 3-D Tensor of shape [batches, num_anchors, num_classes], specifying
+     *      the score of each anchor with each class. Class 0 for each
+     *      [batches, num_anchors, 0] is background and will be ignored.
+     * * 1: A 3-D Tensor of shape [batches, num_anchors, length_box_encoding], with
+     *      the first four values in length_box_encoding specifying the bounding
+     *      box deltas. The box deltas are encoded in the order of [dy, dx, dh, dw],
+     *      where dy and dx is the linear-scale relative correction factor for the
+     *      center position of the bounding box with respect to the width and height,
+     *      dh and dw is the log-scale relative correction factor for the width and
+     *      height. All the entries in length_box_encoding beyond the first four
+     *      values are ignored in this operation.
+     * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+     *      predefined anchor, with format [ctr_y, ctr_x, h, w], where ctr_y and
+     *      ctr_x are the center position of the box, and h and w are the height
+     *      and the width.
+     * * 3: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dy in bounding box deltas.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dx in bounding box deltas.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dh in bounding box deltas.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the scaling
+     *      factor for dw in bounding box deltas.
+     * * 7: An {@link OperandType::BOOL} scalar, set to true to use regular
+     *      multi-class NMS algorithm that do NMS separately for each class,
+     *      set to false for a faster algorithm that only do one single NMS
+     *      using the highest class score..
+     * * 8: An {@link OperandType::INT32} scalar, max_num_detections, specifying
+     *      the maximum number of boxes for the output. Boxes with the lowest
+     *      scores are discarded to meet the limit.
+     * * 9: An {@link OperandType::INT32} scalar, only used when input7 is
+     *      set to false, specifying the maximum number of classes per detection.
+     * * 10: An {@link OperandType::INT32} scalar, only used when input7 is
+     *       set to true, specifying the maximum number of detections when
+     *       applying NMS algorithm for each single class.
+     * * 11: A scalar, score_threshold. Boxes with scores lower than the
+     *       threshold are filtered before sending to the NMS algorithm. The
+     *       scalar must be of {@link OperandType::FLOAT16} if input0 is of
+     *       {@link OperandType::TENSOR_FLOAT16} and of
+     *       {@link OperandType::FLOAT32} if input0 is of
+     *       {@link OperandType::TENSOR_FLOAT32}.
+     * * 12: A scalar, specifying the IoU threshold for hard NMS. The scalar
+     *       must be of {@link OperandType::FLOAT16} if input0 is of
+     *       {@link OperandType::TENSOR_FLOAT16} and of
+     *       {@link OperandType::FLOAT32} if input0 is of
+     *       {@link OperandType::TENSOR_FLOAT32}.
+     * * 13: An {@link OperandType::BOOL} scalar, set to true to include
+     *       background class in the list of label map for the output, set
+     *       to false to not include the background. When the background
+     *       class is included, it has label 0 and the output classes start
+     *       at 1 in the label map, otherwise, the output classes start at 0.
+     *
+     * Outputs:
+     * * 0: A 2-D tensor of the same {@link OperandType} as input0, with shape
+     *      [batches, max_num_detections], specifying the score of each output
+     *      detections.
+     * * 1: A 3-D tensor of shape [batches, max_num_detections, 4], specifying the
+     *      coordinates of each output bounding box, with format
+     *      [y1, x1, y2, x2].
+     * * 2: A 2-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [batches, max_num_detections], specifying the class label for each
+     *      output detection.
+     * * 3: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape [batches],
+     *      specifying the number of valid output detections for each batch.
+     */
+    DETECTION_POSTPROCESSING = 47,
+    /**
+     * For input tensors x and y, computes x == y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    EQUAL = 48,
+    /**
+     * Computes exponential of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    EXP = 49,
+    /**
+     * Inserts a dimension of 1 into a tensor's shape.
+     *
+     * Given a tensor input, this operation inserts a dimension of 1 at the
+     * given dimension index of input's shape. The dimension index starts at
+     * zero; if you specify a negative dimension index, it is counted backward
+     * from the end.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: An {@link OperandType::INT32} scalar specifying the dimension
+     *      index to expand. Must be in the range [-(n + 1), (n + 1)).
+     *
+     * Outputs:
+     * * 0: An (n + 1)-D tensor with the same {@link OperandType} and data as
+     *      input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    EXPAND_DIMS = 50,
+    /**
+     * Gathers values along an axis.
+     *
+     * Produces an output tensor with shape
+     *     input0.dimension[:axis] + indices.dimension + input0.dimension[axis + 1:]
+     * where:
+     *     # Vector indices (output is rank(input0)).
+     *     output[a_0, ..., a_n, i, b_0, ..., b_n] =
+     *       input0[a_0, ..., a_n, indices[i], b_0, ..., b_n]
+     *
+     *     # Higher rank indices (output is rank(input0) + rank(indices) - 1).
+     *     output[a_0, ..., a_n, i, ..., j, b_0, ... b_n] =
+     *       input0[a_0, ..., a_n, indices[i, ..., j], b_0, ..., b_n]
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor from which to gather values.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis.
+     *      Negative index is used to specify axis from the end
+     *      (e.g. -1 for the last axis). Must be in the range [-n, n).
+     * * 2: A k-D tensor {@link OperandType::TENSOR_INT32} of indices.
+     *      The values must be in the bounds of the corresponding dimensions
+     *      of input0.
+     *
+     * Outputs:
+     * * 0: An (n + k - 1)-D tensor with the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    GATHER = 51,
+    /**
+     * Generate aixs-aligned bounding box proposals.
+     *
+     * Bounding box proposals are generated by applying transformation on a set
+     * of predefined anchors with the bounding box deltas from bounding box
+     * regression. A final step of hard NMS is applied to limit the number of
+     * returned boxes.
+     *
+     * Axis-aligned bounding boxes are represented by its upper-left corner
+     * coordinate (x1,y1) and lower-right corner coordinate (x2,y2). A valid
+     * bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Inputs:
+     * * 0: A 4-D Tensor specifying the score of each anchor at each
+     *      location. With "NHWC" data layout, the tensor shape is
+     *      [batches, height, width, num_anchors]. With "NCHW" data layout,
+     *      the tensor shape is [batches, num_anchors, height, width].
+     * * 1: A 4-D Tensor specifying the bounding box deltas. With "NHWC" data
+     *      layout, the tensor shape is [batches, height, width, num_anchors * 4].
+     *      With "NCHW" data layout, the tensor shape is
+     *      [batches, num_anchors * 4, height, width]. The box deltas are encoded
+     *      in the order of [dx, dy, dw, dh], where dx and dy is the linear-scale
+     *      relative correction factor for the center position of the bounding box
+     *      with respect to the width and height, dw and dh is the log-scale
+     *      relative correction factor for the width and height. The last
+     *      dimensions is the channel dimension.
+     * * 2: A 2-D Tensor of shape [num_anchors, 4], specifying the shape of each
+     *      predefined anchor, with format [x1, y1, x2, y2]. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, this tensor should be of
+     *      {@link OperandType::TENSOR_QUANT16_SYMM}, with scale of 0.125.
+     * * 3: A 2-D Tensor of shape [batches, 2], specifying the size of
+     *      each image in the batch, with format [image_height, image_width].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, this
+     *      tensor should be of {@link OperandType::TENSOR_QUANT16_SYMM}, with
+     *      scale of 0.125.
+     * * 4: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of boxes before going into the hard NMS algorithm. Boxes
+     *      with the lowest scores are discarded to meet the limit. Set to
+     *      a non-positive value for unlimited number.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the maximum
+     *      number of boxes returning from the hard NMS algorithm. Boxes
+     *      with the lowest scores are discarded to meet the limit. Set to
+     *      a non-positive value for unlimited number.
+     * * 8: An {@link OperandType::FLOAT32} scalar, specifying the IoU
+     *      threshold for hard NMS.
+     * * 9: An {@link OperandType::FLOAT32} scalar, min_size. Boxes with
+     *      height or width lower than the absolute threshold are filtered out.
+     * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and input1. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, of shape
+     *      [num_output_rois], specifying the score of each output box.
+     *      The boxes are grouped by batches, but the sequential order in
+     *      each batch is not guaranteed. For type of
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, the scale and zero
+     *      point must be the same as input0.
+     * * 1: A tensor of the same {@link OperandType} as input3, of shape
+     *      [num_output_rois, 4], specifying the coordinates of each output
+     *      bounding box for each class, with format [x1, y1, x2, y2].
+     *      The sequential order of the boxes corresponds with output0.
+     *      For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
+     *      scale must be 0.125 and the zero point must be 0.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_output_rois], specifying the batch index of each box. Boxes
+     *      with the same batch index are grouped together.
+     */
+    GENERATE_PROPOSALS = 52,
+    /**
+     * For input tensors x and y, computes x > y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    GREATER = 53,
+    /**
+     * For input tensors x and y, computes x >= y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    GREATER_EQUAL = 54,
+    /**
+     * Performs a grouped 2-D convolution operation.
+     *
+     * Given an input tensor of shape [batches, height, width, depth_in] and a
+     * filter tensor of shape [depth_out, filter_height, filter_width, depth_group]
+     * containing depth_out convolutional filters of depth depth_group, GROUPED_CONV
+     * applies a group of different filters to each input channel group, then
+     * concatenates the results together.
+     *
+     * Specifically, the input channels are divided into num_groups groups, each with
+     * depth depth_group, i.e. depth_in = num_groups * depth_group. The convolutional
+     * filters are also divided into num_groups groups, i.e. depth_out is divisible
+     * by num_groups. GROUPED_CONV applies each group of filters to the corresponding
+     * input channel group, and the result are concatenated together.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, i, j, g * channel_multiplier + q] =
+     *         sum_{di, dj, dk} (
+     *             input[b, strides[1] * i + di, strides[2] * j + dj,
+     *                   g * depth_group + dk] *
+     *             filter[g * channel_multiplier + q, di, dj, dk]
+     *         ) + bias[channel]
+     *
+     * where channel_multiplier = depth_out / num_groups
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 16 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * * 32 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized signed (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input, where depth_in = num_groups * depth_group.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_group], specifying
+     *      the filter, where depth_out must be divisible by num_groups.  For
+     *      tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension (channelDim at
+     *      {@link SymmPerChannelQuantParams}) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, specifying the number of
+     *      groups.
+     * * 10: An {@link OperandType::INT32} scalar, and has to be one of the
+     *       {@link FusedActivationFunc} values. Specifies the activation to
+     *       invoke on the result.
+     * * 11: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input, where depth_in = num_groups * depth_group.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_group], specifying
+     *      the filter, where depth_out must be divisible by num_groups.  For
+     *      tensor of type {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     *      the channel dimension (SymmPerChannelQuantParams::channelDim)
+     *      must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *      the bias should be of {@link OperandType::TENSOR_INT32}, with zeroPoint
+     *      of 0 and bias_scale == input_scale * filter_scale. For filter tensor
+     *      of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}, the bias
+     *      should be of {@link OperandType::TENSOR_INT32}, with zeroPoint of
+     *      0 and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the number of
+     *      groups.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    GROUPED_CONV_2D = 55,
+    /**
+     * Localize the maximum keypoints from heatmaps.
+     *
+     * This operation approximates the accurate maximum keypoint scores and
+     * indices after bicubic upscaling by using Taylor expansion up to the
+     * quadratic term.
+     *
+     * The bounding box is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A valid bounding box should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D Tensor of shape
+     *      [num_boxes, heatmap_size, heatmap_size, num_keypoints],
+     *      specifying the heatmaps, the height and width of heatmaps should
+     *      be the same, and must be greater than or equal to 2.
+     * * 1: A 2-D Tensor of shape [num_boxes, 4], specifying the bounding boxes,
+     *      each with format [x1, y1, x2, y2]. For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM}, this tensor should
+     *      be of {@link OperandType::TENSOR_QUANT16_ASYMM}, with zeroPoint
+     *      of 0 and scale of 0.125.
+     *      For input0 of type
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}, this tensor
+     *      should be of {@link OperandType::TENSOR_QUANT16_ASYMM}, with
+     *      zeroPoint of -128 and scale of 0.125.
+     * * 2: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0, with shape
+     *      [num_boxes, num_keypoints], specifying score of the keypoints.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} or
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from input0 scale and zeroPoint.
+     * * 1: A tensor of the same {@link OperandType} as input1, with shape
+     *      [num_boxes, num_keypoints, 2], specifying the location of
+     *      the keypoints, the second dimension is organized as
+     *      [keypoint_x, keypoint_y].
+     *      For type of {@link OperandType::TENSOR_QUANT16_ASYMM}, the
+     *      scale must be 0.125 and the zero point must be 0.
+     */
+    HEATMAP_MAX_KEYPOINT = 56,
+    /**
+     * Applies instance normalization to the input tensor.
+     *
+     * The values in the output tensor are computed as:
+     *
+     *     output[b, h, w, c] =
+     *         (input[b, h, w, c] - mean[b, c]) * gamma /
+     *         sqrt(var[b, c] + epsilon) + beta
+     *
+     * Where the mean and variance are computed across the spatial dimensions:
+     *
+     *     mean[b, c] =
+     *         sum_{h, w}(input[b, h, w, c]) / sum(1)
+     *
+     *     var[b, c] =
+     *         sum_{h, w}(pow(input[b, h, w, c] - mean[b, c], 2)) / sum(1)
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be normalized.
+     * * 1: A scalar, specifying gamma, the scale applied to the normalized
+     *      tensor. The scalar must be of {@link OperandType::FLOAT16} if
+     *      input0 is of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} if input0 is of
+     *      {@link OperandType::TENSOR_FLOAT32}.
+     * * 2: A scalar, specifying beta, the offset applied to the normalized
+     *      tensor. The scalar must be of {@link OperandType::FLOAT16} if
+     *      input0 is of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} if input0 is of
+     *      {@link OperandType::TENSOR_FLOAT32}.
+     * * 3: A scalar, specifying epsilon, the small value added to variance to
+     *      avoid dividing by zero. The scalar must be of {@link OperandType::FLOAT16} if
+     *      input0 is of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} if input0 is of
+     *      {@link OperandType::TENSOR_FLOAT32}.
+     * * 4: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} and same shape as input0.
+     */
+    INSTANCE_NORMALIZATION = 57,
+    /**
+     * For input tensors x and y, computes x < y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    LESS = 58,
+    /**
+     * For input tensors x and y, computes x <= y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    LESS_EQUAL = 59,
+    /**
+     * Computes natural logarithm of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    LOG = 60,
+    /**
+     * Returns the truth value of x AND y element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+     *      compatible with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    LOGICAL_AND = 61,
+    /**
+     * Computes the truth value of NOT x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    LOGICAL_NOT = 62,
+    /**
+     * Returns the truth value of x OR y element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     * * 1: A tensor of {@link OperandType::TENSOR_BOOL8} and dimensions
+     *      compatible with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    LOGICAL_OR = 63,
+    /**
+     * Computes the log softmax activations given logits.
+     *
+     * The output is calculated using this formula:
+     *
+     *     output = logits * beta - log(reduce_sum(exp(logits * beta), axis))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor specifying the input logits.
+     * * 1: A scalar, specifying the positive scaling factor for the exponent,
+     *      beta.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the beta
+     *      value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the beta
+     *      value must be of {@link OperandType::FLOAT32}.
+     * * 2: An {@link OperandType::INT32} scalar specifying the axis to
+     *      reduce across. Negative index is used to specify axis from the
+     *      end (e.g. -1 for the last axis). Must be in the range [-n, n).
+     *
+     * Outputs:
+     * * 0: The output tensor of the same {@link OperandType} and shape as
+     *      input0.
+     */
+    LOG_SOFTMAX = 64,
+    /**
+     * Returns the element-wise maximum of two tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+     *      with input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scales and zeroPoint can be different from input0 scale and zeroPoint.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    MAXIMUM = 65,
+    /**
+     * Returns the element-wise minimum of two tensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and compatible dimensions
+     *      with input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scales and zeroPoint can be different from input0 scale and zeroPoint.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    MINIMUM = 66,
+    /**
+     * Computes numerical negative value element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    NEG = 67,
+    /**
+     * For input tensors x and y, computes x != y elementwise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * This operation supports broadcasting.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     * * 1: A tensor of the same {@link OperandType} and dimensions compatible
+     *      with input0.
+     *
+     * Outputs:
+     * * 0: A tensor of {@link OperandType::TENSOR_BOOL8}.
+     */
+    NOT_EQUAL = 68,
+    /**
+     * Pads a tensor with the given constant value according to the specified
+     * paddings.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor, specifying the tensor to be padded.
+     * * 1: A 2-D Tensor of {@link OperandType::TENSOR_INT32}, the paddings
+     *      for each spatial dimension of the input tensor. The shape of the
+     *      tensor must be {rank(input0), 2}.
+     *      padding[i, 0] specifies the number of elements to be padded in the
+     *      front of dimension i.
+     *      padding[i, 1] specifies the number of elements to be padded after
+     *      the end of dimension i.
+     * * 2: A scalar specifying the value to use for padding input0.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16}, the
+     *      pad value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32}, the
+     *      pad value must be of {@link OperandType::FLOAT32}.
+     *      For input tensor of {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the pad value must be of {@link OperandType::INT32}. The
+     *      scale and zeroPoint are assumed to be the same as in input0.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The
+     *      output tensor has the same rank as input0, and each
+     *      dimension of the output tensor has the same size as the
+     *      corresponding dimension of the input tensor plus the size
+     *      of the padding:
+     *          output0.dimension[i] =
+     *              padding[i, 0] + input0.dimension[i] + padding[i, 1]
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    PAD_V2 = 69,
+    /**
+     * Computes the power of one value to another.
+     *
+     * Given a tensor base and a tensor exponent, this operation computes
+     * base^exponent elementwise.
+     *
+     * This operations supports broadcasting. The size of the output is the
+     * maximum size along each dimension of the input operands. It starts with
+     * the trailing dimensions, and works its way forward.
+     *
+     * For example:
+     *     base.dimension     =    {4, 1, 2}
+     *     exponent.dimension = {5, 4, 3, 1}
+     *     output.dimension   = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor specifying the base.
+     * * 1: A tensor specifying the exponent.
+     *
+     * Outputs:
+     * * 0: An output tensor.
+     */
+    POW = 70,
+    /**
+     * Parametric Rectified Linear Unit.
+     *
+     * It follows: f(x) = alpha * x for x < 0, f(x) = x for x >= 0, where alpha
+     * is a learned array with the same {@link OperandType} and compatible
+     * dimensions as input x.
+     *
+     * Two dimensions are compatible when:
+     *     1. they are equal, or
+     *     2. one of them is 1
+     *
+     * The size of the output is the maximum size along each dimension of the
+     * input operands. It starts with the trailing dimensions, and works its way
+     * forward.
+     *
+     * Example:
+     *     input.dimension  =    {4, 1, 2}
+     *     alpha.dimension  = {5, 4, 3, 1}
+     *     output.dimension = {5, 4, 3, 2}
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input.
+     * * 1: A tensor of the same {@link OperandType}, and compatible dimensions
+     *      as input0, specifying the alpha.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scales and zeroPoint can be different from input0 scale and zeroPoint.
+     */
+    PRELU = 71,
+    /**
+     * Quantizes the input tensor.
+     *
+     * The formula for {@link OperandType::TENSOR_QUANT8_ASYMM} output tensor is:
+     *
+     *     output = max(0, min(255, round(input / scale) + zeroPoint)
+     *
+     * The formula for {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} output
+     * tensor is:
+     *
+     *     output = max(-128, min(127, round(input / scale) + zeroPoint)
+     *
+     * Supported input tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported output tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor, may be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0, but with
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM} or.
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}.
+     */
+    QUANTIZE = 72,
+    /**
+     * A version of quantized LSTM, using 16 bit quantization for internal
+     * state.
+     *
+     * There is no projection layer, so cell state size is equal to the output
+     * size.
+     *
+     * Inputs:
+     * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [numBatches, inputSize] specifying the input to the LSTM
+     *      cell. Tensor is quantized with a fixed quantization range of
+     *      [-1, 127/128] (scale = 1/128, zeroPoint = 128).
+     * * 1: The input-to-input weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-input part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 2: The input-to-forget weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-forget part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 3: The input-to-cell weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-cell part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 4: The input-to-output weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, inputSize] specifying input-to-output part of
+     *      weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 5: The recurrent-to-input weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, outputSize] specifying recurrent-to-input part
+     *      of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 6: The recurrent-to-forget weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, outputSize] specifying recurrent-to-forget
+     *      part of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 7: The recurrent-to-cell weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, outputSize] specifying recurrent-to-cell part
+     *      of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 8: The recurrent-to-output weights.
+     *      A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [outputSize, outputSize] specifying recurrent-to-output
+     *      part of weights for fully-connected layer inside the LSTM cell.
+     *      Quantization zero point and scale must be the same across all the
+     *      weights.
+     * * 9: The input gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 10:The forget gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 11:The cell bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 12:The output gate bias.
+     *      A 1-D tensor of type {@link OperandType::TENSOR_INT32} and shape
+     *      [outputSize] specifying the bias for the fully-connected layer
+     *      inside the LSTM cell. Bias is quantized with scale being a product
+     *      of input and weights scales and zeroPoint equal to 0.
+     * * 13: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       and shape [numBatches, outputSize] specifying the cell state from the
+     *       previous time step of the LSTM cell. It is quantized using a
+     *       quantization range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 /
+     *       32768, zeroPoint = 0).
+     * * 14: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *       and shape [numBathes, outputSize] specifying the output of the LSTM
+     *       cell from previous time-step. Tensor is quantized with a fixed
+     *       quantization range of [-1, 127/128] (scale = 1/128, zeroPoint =
+     *       128).
+     *
+     *
+     * Outputs:
+     * * 0: A 2-D tensor of type {@link OperandType::TENSOR_QUANT16_SYMM}
+     *      and shape [numBatches, outputSize] which contains a cell state from
+     *      the current time step. Tensor is quantized using a quantization
+     *      range of [-2^4, 2^4 * 32767/32768] (scale = 2^4 / 32768, zeroPoint =
+     *      0).
+     * * 1: A 2-D tensor of type {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and shape [numBathes, outputSize] which contains the output value.
+     *      Tensor is quantized with a fixed quantization range of [-1, 127/128]
+     *      (scale = 1/128, zeroPoint = 128).
+     */
+    QUANTIZED_16BIT_LSTM = 73,
+    /**
+     * Draws samples from a multinomial distribution.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Inputs:
+     * * 0: A 2-D tensor with shape [batches, classes], specifying the
+     *      unnormalized log-probabilities for all classes.
+     * * 1: A scalar {@link OperandType::INT32}, specifying the number of
+     *      independent samples to draw for each row slice.
+     * * 2: A 1-D {@link OperandType::TENSOR_INT32} tensor with shape [2],
+     *      specifying seeds used to initialize the random distribution. If both
+     *      provided seeds are 0, both will be randomly generated.
+     * Outputs:
+     * * 0: A 2-D {@link OperandType::TENSOR_INT32} tensor with shape
+     *      [batches, samples], containing the drawn samples.
+     */
+    RANDOM_MULTINOMIAL = 74,
+    /**
+     * Reduces a tensor by computing the "logical and" of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     */
+    REDUCE_ALL = 75,
+    /**
+     * Reduces a tensor by computing the "logical or" of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_BOOL8}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     */
+    REDUCE_ANY = 76,
+    /**
+     * Reduces a tensor by computing the maximum of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    REDUCE_MAX = 77,
+    /**
+     * Reduces a tensor by computing the minimum of elements along given
+     * dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    REDUCE_MIN = 78,
+    /**
+     * Reduces a tensor by multiplying elements along given dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     */
+    REDUCE_PROD = 79,
+    /**
+     * Reduces a tensor by summing elements along given dimensions.
+     *
+     * If keep_dims is true, the reduced dimensions are
+     * retained with length 1. Otherwise, the rank of the tensor is reduced by
+     * 1 for each entry in dimensions.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: up to 4
+     *
+     * Inputs:
+     * * 0: An n-D tensor.
+     * * 1: A 1-D tensor of {@link OperandType::TENSOR_INT32}. The dimensions
+     *      to reduce. Dimension values must be in the range [-n, n).
+     * * 2: An {@link OperandType::BOOL} scalar, keep_dims. If true,
+     *      retains reduced dimensions with length 1.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0.
+     *      If all dimensions are reduced and keep_dims is false, the output
+     *      shape is [1].
+     */
+    REDUCE_SUM = 80,
+    /**
+     * Select and scale the feature map of each region of interest to a unified
+     * output size by average pooling sampling points from bilinear interpolation.
+     *
+     * The region of interest is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A spatial scaling factor is applied to map into feature map coordinate.
+     * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * No rounding is applied in this operation. The sampling points are unified
+     * distributed in the pooling bin and their values are calculated by bilinear
+     * interpolation.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, specifying the feature map.
+     * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+     *      the regions of interest, each line with format [x1, y1, x2, y2].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM},
+     *      this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      with zeroPoint of 0 and scale of 0.125. Zero num_rois is
+     *      supported for this tensor.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together. Zero num_rois is
+     *      supported for this tensor.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the number of
+     *      sampling points in height dimension used to compute the output.
+     *      Set to 0 for adaptive value of ceil(roi_height/out_height).
+     * * 8: An {@link OperandType::INT32} scalar, specifying the number of
+     *      sampling points in width dimension used to compute the output.
+     *      Set to 0 for adaptive value of ceil(roi_width/out_width).
+     * * 9: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The output
+     *      shape is [num_rois, out_height, out_width, depth].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from the input0 scale and zeroPoint.
+     */
+    ROI_ALIGN = 81,
+    /**
+     * Select and scale the feature map of each region of interest to a unified
+     * output size by max-pooling.
+     *
+     * The region of interest is represented by its upper-left corner coordinate
+     * (x1,y1) and lower-right corner coordinate (x2,y2) in the original image.
+     * A spatial scaling factor is applied to map into feature map coordinate.
+     * A valid region of interest should satisfy x1 <= x2 and y1 <= y2.
+     *
+     * Rounding is applied in this operation to ensure integer boundary for
+     * regions of interest and pooling bins.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Inputs:
+     * * 0: A 4-D tensor, specifying the feature map.
+     * * 1: A 2-D Tensor of shape [num_rois, 4], specifying the locations of
+     *      the regions of interest, each line with format [x1, y1, x2, y2].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      this tensor should be of {@link OperandType::TENSOR_QUANT16_ASYMM},
+     *      with zeroPoint of 0 and scale of 0.125.
+     * * 2: An 1-D {@link OperandType::TENSOR_INT32} tensor, of shape
+     *      [num_rois], specifying the batch index of each box. Boxes with
+     *      the same batch index are grouped together.
+     * * 3: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 5: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the height of original image to the height of feature map.
+     * * 6: An {@link OperandType::FLOAT32} scalar, specifying the ratio
+     *      from the width of original image to the width of feature map.
+     * * 7: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: A tensor of the same {@link OperandType} as input0. The output
+     *      shape is [num_rois, out_height, out_width, depth].
+     *      For input0 of type {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    ROI_POOLING = 82,
+    /**
+     * Computes reciprocal of square root of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    RSQRT = 83,
+    /**
+     * Using a tensor of booleans c and input tensors x and y select values
+     * elementwise from both input tensors:
+     *
+     * O[i] = C[i] ? x[i] : y[i].
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: A tensor of type {@link OperandType::TENSOR_BOOL8} acting as a
+     *      mask that chooses, based on the value at each element, whether the
+     *      corresponding element in the output should be taken from input1 (if
+     *      true) or input2 (if false).
+     * * 1: An input tensor of the same shape as input0.
+     * * 2: An input tensor of the same shape and type as input1.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scales and zeroPoint can be different from input1 scale and zeroPoint.
+     *
+     * Outputs:
+     * * 0: A tensor of the same type and shape as input1 and input2.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    SELECT = 84,
+    /**
+     * Computes sin of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    SIN = 85,
+    /**
+     * Extracts a slice of specified size from the input tensor starting at a
+     * specified location.
+     *
+     * The starting location is specified as a 1-D tensor containing offsets
+     * for each dimension. The size is specified as a 1-D tensor containing
+     * either size of a slice along corresponding dimension or -1. In the latter
+     * case, all the remaining elements in dimension are included in the slice.
+     *
+     * A sum of begin offset and a size of a slice must not exceed size of a
+     * corresponding dimension.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor to take slice from, may be zero-sized.
+     * * 1: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+     *      the beginning indices of the slice in each dimension.
+     * * 2: A 1-D tensor of type {@link OperandType::TENSOR_INT32} specifying
+     *      the size of the slice in each dimension.
+     *
+     * Outputs:
+     * * 0: An n-D tensor of the same type as the input containing the slice.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      its scale and zeroPoint has to be same as the input0 scale and zeroPoint.
+     */
+    SLICE = 86,
+    /**
+     * Splits a tensor along a given axis into num_splits subtensors.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: An n-D tensor to split.
+     * * 1: An {@link OperandType::INT32} scalar specifying the axis along
+     *      which to split.
+     * * 2: An {@link OperandType::INT32} scalar indicating the number of
+     *      splits along given axis. Must evenly divide axis size.
+     *
+     * Outputs:
+     * * 0 ~ (num_splits - 1): Resulting subtensors.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    SPLIT = 87,
+    /**
+     * Computes square root of x element-wise.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape as input0.
+     */
+    SQRT = 88,
+    /**
+     * Constructs a tensor by tiling a given tensor.
+     *
+     * This operation creates a new tensor by replicating `input` `multiples`
+     * times. The output tensor's i-th dimension has `input.dims(i) * multiples[i]`
+     * elements, and the values of `input` are replicated `multiples[i]` times
+     * along the i-th dimension.
+     * For example, tiling `[a b c d]` by `[2]` produces `[a b c d a b c d]`.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: input, an n-D tensor specifying the input.
+     * * 1: multiples, a 1-D tensor of {@link OperandType::TENSOR_INT32}.
+     *      The length of multiples must be n.
+     *
+     * Outputs:
+     * * 0: A tiled tensor of the same {@link OperandType} and rank as `input`.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    TILE = 89,
+    /**
+     * Finds values and indices of the k largest entries for the last dimension.
+     *
+     * Resulting values in each dimensions are sorted in descending order. If
+     * two values are equal, the one with larger index appears first.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: from 1
+     *
+     * Inputs:
+     * * 0: input, an n-D tensor specifying the input.
+     * * 1: k, an {@link OperandType::INT32} scalar, specifying the number of
+     *      top elements to look for along the last dimension.
+     *
+     * Outputs:
+     * * 0: An n-D tensor of the same type as the input, containing the k
+     *      largest elements along each last dimensional slice.
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     * * 1: An n-D tensor of type {@link OperandType::TENSOR_INT32}
+     *      containing the indices of values within the last dimension of input.
+     */
+    TOPK_V2 = 90,
+    /**
+     * Performs the transpose of 2-D convolution operation.
+     *
+     * This operation is sometimes called "deconvolution" after Deconvolutional
+     * Networks, but is actually the transpose (gradient) of
+     * {@link OperandType::CONV_2D} rather than an actual deconvolution.
+     *
+     * The output dimensions are functions of the filter dimensions, stride, and
+     * padding.
+     *
+     * Supported tensor {@link OperandType} configurations:
+     * * 16 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT16} for input, filter, output, and bias.
+     *
+     * * 32 bit floating point:
+     * * * {@link OperandType::TENSOR_FLOAT32} for input, filter, output, and bias.
+     *
+     * * Quantized:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized with symmetric per channel quantization for the filter:
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Available since HAL version 1.3:
+     * * Quantized signed (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, filter, and output.
+     * * * {@link OperandType::TENSOR_INT32} for bias (with scale set to
+     * * * input.scale * filter.scale).
+     *
+     * * Quantized signed with filter symmetric per channel quantization (since HAL version 1.3):
+     * * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} for input, and output.
+     * * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} for filter.
+     * * * {@link OperandType::TENSOR_INT32} for bias (scale set to 0.0,
+     * * * each value scaling is separate and equal to input.scale * filter.scales[channel]).
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both explicit padding and implicit padding are supported.
+     *
+     * Inputs (explicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (SymmPerChannelQuantParams::channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias must be of the
+     *      same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32},
+     *      with zeroPoint of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias must be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the left, in the ‘width’ dimension.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the right, in the ‘width’ dimension.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the top, in the ‘height’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the padding on
+     *      the bottom, in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 8: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 9: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 10: An {@link OperandType::BOOL} scalar, set to true to specify
+     *       NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Inputs (implicit padding):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth_in],
+     *      specifying the input.
+     * * 1: A 4-D tensor, of shape
+     *      [depth_out, filter_height, filter_width, depth_in], specifying the
+     *      filter. For tensor of type
+     *      {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL} the channel
+     *      dimension (SymmPerChannelQuantParams::channelDim) must be set to 0.
+     * * 2: A 1-D tensor, of shape [depth_out], specifying the bias. For input
+     *      tensor of type {@link OperandType::TENSOR_FLOAT32} or
+     *      {@link OperandType::TENSOR_FLOAT16}, the bias should be of the
+     *      same type.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_ASYMM}
+     *      and {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+     *      the bias should be of {@link OperandType::TENSOR_INT32},
+     *      with zeroPoint of 0 and bias_scale == input_scale * filter_scale.
+     *      For filter tensor of {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL},
+     *      the bias must be of {@link OperandType::TENSOR_INT32}, with zeroPoint of 0
+     *      and bias_scale of 0. The actual scale of each value 'i' is equal to
+     *      bias_scale[i] = input_scale * filter_scale[i].
+     * * 3: An {@link OperandType::TENSOR_INT32} tensor, specifying the output
+     *      tensor shape.
+     * * 4: An {@link OperandType::INT32} scalar, specifying the implicit
+     *      padding scheme, has to be one of the
+     *      following values: {0 (NONE), 1 (SAME), 2 (VALID)}.
+     * * 5: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘width’ dimension.
+     * * 6: An {@link OperandType::INT32} scalar, specifying the stride when
+     *      walking through input in the ‘height’ dimension.
+     * * 7: An {@link OperandType::INT32} scalar, and has to be one of the
+     *      {@link FusedActivationFunc} values. Specifies the activation to
+     *      invoke on the result.
+     * * 8: An {@link OperandType::BOOL} scalar, set to true to specify
+     *      NCHW data layout for input0 and output0. Set to false for NHWC.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, out_height, out_width, depth_out].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint can be different from inputs' scale and zeroPoint.
+     */
+    TRANSPOSE_CONV_2D = 91,
+    /**
+     * A recurrent neural network specified by an LSTM cell.
+     *
+     * Performs (fully) dynamic unrolling of input.
+     *
+     * This Op unrolls the input along the time dimension, and implements the
+     * following operation for each element in the sequence
+     * s = 1...sequence_length:
+     *   outputs[s] = projection(state = activation(LSTMOp(inputs[s])))
+     *
+     * Where LSTMOp is the LSTM op as in {@link OperandType::LSTM},
+     * the "projection" is an optional projection layer from state and output
+     * and the “activation” is the function passed as the
+     * “fused_activation_function” argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: 3, either time-major or batch-major.
+     *
+     * All input and output tensors must be of the same type.
+     *
+     * Inputs:
+     * * 0: The input (\f$x_t\f$).
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, input_size]
+     *        If batch-major: [batch_size, max_time, input_size]
+     *      where “max_time” is the number of timesteps (sequence length),
+     *      “batch_size” corresponds to the batching dimension, and
+     *      “input_size” is the size of the input.
+     * * 1: The input-to-input weights (\f$W_{xi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, input_size], where “num_units”
+     *      corresponds to the number of cell units.
+     * * 2: The input-to-forget weights (\f$W_{xf}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 3: The input-to-cell weights (\f$W_{xc}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 4: The input-to-output weights (\f$W_{xo}\f$).
+     *      A 2-D tensor of shape [num_units, input_size].
+     * * 5: The recurrent-to-input weights (\f$W_{hi}\f$). Optional.
+     *      A 2-D tensor of shape [num_units, output_size], where “output_size”
+     *      corresponds to either the number of cell units (i.e., “num_units”),
+     *      or the second dimension of the “projection_weights”, if defined.
+     * * 6: The recurrent-to-forget weights (\f$W_{hf}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 7: The recurrent-to-cell weights (\f$W_{hc}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 8: The recurrent-to-output weights (\f$W_{ho}\f$).
+     *      A 2-D tensor of shape [num_units, output_size].
+     * * 9: The cell-to-input weights (\f$W_{ci}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 10:The cell-to-forget weights (\f$W_{cf}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 11:The cell-to-output weights (\f$W_{co}\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 12:The input gate bias (\f$b_i\f$). Optional.
+     *      A 1-D tensor of shape [num_units].
+     * * 13:The forget gate bias (\f$b_f\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 14:The cell bias (\f$b_c\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 15:The output gate bias (\f$b_o\f$).
+     *      A 1-D tensor of shape [num_units].
+     * * 16:The projection weights (\f$W_{proj}\f$). Optional.
+     *      A 2-D tensor of shape [output_size, num_units].
+     * * 17:The projection bias (\f$b_{proj}\f$). Optional.
+     *      A 1-D tensor of shape [output_size].
+     * * 18:The output state (in) (\f$h_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, output_size].
+     * * 19:The cell state (in) (\f$C_{t-1}\f$).
+     *      A 2-D tensor of shape [batch_size, num_units].
+     * * 20:The activation function (\f$g\f$).
+     *      A value indicating the activation function:
+     *      <ul>
+     *      <li>0: None;
+     *      <li>1: Relu;
+     *      <li>3: Relu6;
+     *      <li>4: Tanh;
+     *      <li>6: Sigmoid.
+     *      </ul>
+     * * 21:The clipping threshold (\f$t_{cell}\f$) for the cell state, such
+     *      that values are bound within [-cell_clip, cell_clip]. If set to 0.0
+     *      then clipping is disabled.
+     * * 22:The clipping threshold (\f$t_{proj}\f$) for the output from the
+     *      projection layer, such that values are bound within
+     *      [-proj_clip, proj_clip]. If set to 0.0 then clipping is disabled.
+     * * 23:Time-major if true, batch-major if false.
+     * * 24:The input layer normalization weights. Optional.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at input gate.
+     * * 25:The forget layer normalization weights. Optional.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at forget gate.
+     * * 26:The cell layer normalization weights. Optional.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at cell gate.
+     * * 27:The output layer normalization weights. Optional.
+     *      A 1-D tensor of shape [num_units]. Used to rescale normalized inputs
+     *      to activation at output gate.
+     *
+     * Outputs:
+     * * 0: The output (\f$o_t\f$).
+     *      A 3-D tensor of shape:
+     *        If time-major: [max_time, batch_size, output_size]
+     *        If batch-major: [batch_size, max_time, output_size]
+     * * 1: A tensor of shape [batch_size, output_size] containing a hidden
+     *      state from the last time step in the sequence. This output is
+     *      optional and can be omitted. If this output is present then
+     *      output #2 must be present as well.
+     *      Available since HAL version 1.3.
+     * * 2: A tensor of shape [batch_size, cell_size] containing a cell state
+     *      from the last time step in the sequence. This output is optional
+     *      and can be omitted.
+     *      Available since HAL version 1.3.
+     */
+    UNIDIRECTIONAL_SEQUENCE_LSTM = 92,
+    /**
+     * A recurrent neural network layer that applies a basic RNN cell to a
+     * sequence of inputs.
+     *
+     * This layer unrolls the input along the sequence dimension, and implements
+     * the following operation
+     * for each element in the sequence s = 1...sequence_length:
+     *   outputs[s] = state = activation(inputs[s] * input_weights’ + state *
+     *   recurrent_weights’ + bias)
+     *
+     * Where:
+     * * “input_weights” is a weight matrix that multiplies the inputs;
+     * * “recurrent_weights” is a weight matrix that multiplies the current
+     *    “state” which itself is the output from the previous time step
+     *    computation;
+     * * “bias” is a bias vector (added to each output vector in the batch);
+     * * “activation” is the function passed as the “fused_activation_function”
+     *   argument (if not “NONE”).
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * The input tensors must all be the same type.
+     *
+     * Inputs:
+     * * 0: input.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to 1, then the input has a shape [maxTime, batchSize,
+     *      inputSize], otherwise the input has a shape [batchSize, maxTime,
+     *      inputSize].
+     * * 1: weights.
+     *      A 2-D tensor of shape [numUnits, inputSize].
+     * * 2: recurrent_weights.
+     *      A 2-D tensor of shape [numUnits, numUnits].
+     * * 3: bias.
+     *      A 1-D tensor of shape [numUnits].
+     * * 4: hidden state
+     *      A 2-D tensor of shape [batchSize, numUnits]. Specifies a hidden
+     *      state input for the first time step of the computation.
+     * * 5: fusedActivationFunction.
+     *      A {@link FusedActivationFunc} value indicating the activation function. If
+     *      “NONE” is specified then it results in a linear activation.
+     * * 6: timeMajor
+     *      An {@link OperandType::INT32} scalar specifying the shape format
+     *      of input and output tensors. Must be set to either 0 or 1.
+     * Outputs:
+     * * 0: output.
+     *      A 3-D tensor. The shape is defined by the input 6 (timeMajor). If
+     *      it is set to 1, then the output has a shape [maxTime, batchSize,
+     *      numUnits], otherwise the output has a shape [batchSize, maxTime,
+     *      numUnits].
+     * * 1: A tensor of shape [batchSize, numUnits] containing hidden state
+     *      from the last time step in the sequence. This output is optional
+     *      and can be omitted.
+     *      Available since HAL version 1.3.
+     */
+    UNIDIRECTIONAL_SEQUENCE_RNN = 93,
+    /**
+     * Resizes images to given size using the nearest neighbor interpretation.
+     *
+     * Resized images must be distorted if their output aspect ratio is not the
+     * same as input aspect ratio. The corner pixels of output may not be the
+     * same as corner pixels of input.
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} (since HAL version 1.3)
+     *
+     * Supported tensor rank: 4, with "NHWC" or "NCHW" data layout.
+     * With the default data layout NHWC, the data is stored in the order of:
+     * [batch, height, width, channels]. Alternatively, the data layout could
+     * be NCHW, the data storage order of: [batch, channels, height, width].
+     *
+     * Both resizing by shape and resizing by scale are supported.
+     *
+     * Inputs (resizing by shape):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input. Zero batches is supported for this tensor.
+     * * 1: An {@link OperandType::INT32} scalar, specifying the output
+     *      width of the output tensor.
+     * * 2: An {@link OperandType::INT32} scalar, specifying the output
+     *      height of the output tensor.
+     * * 3: An {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
+     *
+     * Inputs (resizing by scale):
+     * * 0: A 4-D tensor, of shape [batches, height, width, depth], specifying
+     *      the input. Zero batches is supported for this tensor.
+     * * 1: A scalar, specifying width_scale, the scaling factor of the width
+     *      dimension from the input tensor to the output tensor. The output
+     *      width is calculated as new_width = floor(width * width_scale).
+     *      The scalar must be of {@link OperandType::FLOAT16} if input0 is
+     *      of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} otherwise.
+     * * 2: A scalar, specifying height_scale, the scaling factor of the height
+     *      dimension from the input tensor to the output tensor. The output
+     *      height is calculated as new_height = floor(height * height_scale).
+     *      The scalar must be of {@link OperandType::FLOAT16} if input0 is
+     *      of {@link OperandType::TENSOR_FLOAT16} and of
+     *      {@link OperandType::FLOAT32} otherwise.
+     * * 3: An {@link OperandType::BOOL} scalar, default to false.
+     *      Set to true to specify NCHW data layout for input0 and output0.
+     * * 4: Align corners. An optional {@link OperandType::BOOL}
+     *      scalar, default to false.  If True, the centers of the 4 corner
+     *      pixels of the input and output tensors are aligned, preserving the
+     *      values at the corner pixels.
+     *      Available since HAL version 1.3.
+     * * 5: Half pixel centers. An optional {@link OperandType::BOOL}
+     *      scalar, default to false. If True, the pixel centers are assumed to
+     *      be at (0.5, 0.5). This is the default behavior of image.resize in
+     *      TF 2.0. If this parameter is True, then align_corners parameter
+     *      must be False.
+     *      Available since HAL version 1.3.
+     *
+     * Outputs:
+     * * 0: The output 4-D tensor, of shape
+     *      [batches, new_height, new_width, depth].
+     *      For a {@link OperandType::TENSOR_QUANT8_ASYMM} and
+     *      {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED} tensor,
+     *      the scale and zeroPoint must be the same as input0.
+     */
+    RESIZE_NEAREST_NEIGHBOR = 94,
+    /**
+     * Quantized version of {@link OperationType::LSTM}.
+     *
+     * The input and the output use asymmetric quantized types, while the rest
+     * use symmetric ones.
+     *
+     * Inputs:
+     * * 0: The input to the LSTM cell.
+     *      Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *      Shape: [batchSize, inputSize]
+     * * 1: The input-to-input weights. Optional.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, inputSize]
+     * * 2: The input-to-forget weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, inputSize]
+     * * 3: The input-to-cell weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, inputSize]
+     * * 4: The input-to-output weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, inputSize]
+     * * 5: The recurrent-to-input weights. Optional.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, outputSize]
+     * * 6: The recurrent-to-forget weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, outputSize]
+     * * 7: The recurrent-to-cell weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, outputSize]
+     * * 8: The recurrent-to-output weights.
+     *      Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *      Shape: [numUnits, outputSize]
+     * * 9: The cell-to-input weights (for peephole). Optional.
+     *      Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *      Shape: [numUnits]
+     * * 10: The cell-to-forget weights (for peephole). Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 11: The cell-to-output weights (for peephole). Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 12: The input gate bias. Quantized with scale being the
+     *       product of input and weights scales and zeroPoint equal to 0.
+     *       Optional.
+     *       Type: {@link OperandType::TENSOR_INT32}
+     *       Shape: [numUnits]
+     * * 13: The forget gate bias. Quantized with scale being the
+     *       product of input and weights scales and zeroPoint equal to 0.
+     *       Type: {@link OperandType::TENSOR_INT32}
+     *       Shape: [numUnits]
+     * * 14: The cell bias. Quantized with scale being the
+     *       product of input and weights scales and zeroPoint equal to 0.
+     *       Type: {@link OperandType::TENSOR_INT32}
+     *       Shape: [numUnits]
+     * * 15: The output gate bias. Quantized with scale being the
+     *       product of input and weights scales and zeroPoint equal to 0.
+     *       Type: {@link OperandType::TENSOR_INT32}
+     *       Shape: [numUnits]
+     * * 16: The projection weights. Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT8_SYMM}
+     *       Shape: [outputSize, numUnits]
+     * * 17: The projection bias. Quantized with scale being the
+     *       product of input and weights scales and zeroPoint equal to 0.
+     *       Optional.
+     *       Type: {@link OperandType::TENSOR_INT32}
+     *       Shape: [outputSize]
+     * * 18: The output from the previous time step.
+     *       Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *       Shape: [batchSize, outputSize]
+     * * 19: The cell state from the previous time step.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [batchSize, numUnits]
+     * * 20: The input layer normalization weights. Used to rescale
+     *       normalized inputs to activation at input gate. Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 21: The forget layer normalization weights. Used to
+     *       rescale normalized inputs to activation at forget gate. Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 22: The cell layer normalization weights. Used to rescale
+     *       normalized inputs to activation at cell gate. Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 23: The output layer normalization weights. Used to
+     *       rescale normalized inputs to activation at output gate. Optional.
+     *       Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *       Shape: [numUnits]
+     * * 24: The cell clip. If provided the cell state is clipped
+     *       by this value prior to the cell output activation. Optional.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 25: The projection clip. If provided and projection is enabled,
+     *       this is used for clipping the projected values. Optional.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 26: The scale of the intermediate result of matmul,
+     *       i.e. input to layer normalization, at input gate.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 27: The scale of the intermediate result of matmul,
+     *       i.e. input to layer normalization, at forget gate.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 28: The scale of the intermediate result of matmul,
+     *       i.e. input to layer normalization, at cell gate.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 29: The scale of the intermediate result of matmul,
+     *       i.e. input to layer normalization, at output gate.
+     *       Type: {@link OperandType::FLOAT32}.
+     * * 30: The zero point of the hidden state, i.e. input to
+     *       projection.
+     *       Type: {@link OperandType::INT32}.
+     * * 31: The scale of the hidden state, i.e. input to
+     *       projection.
+     *       Type: {@link OperandType::FLOAT32}.
+     *
+     * Outputs:
+     * * 0: The output state (out).
+     *      Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *      Shape: [batchSize, outputSize]
+     * * 1: The cell state (out).
+     *      Type: {@link OperandType::TENSOR_QUANT16_SYMM}
+     *      Shape: [batchSize, numUnits]
+     * * 2: The output. This is effectively the same as the current
+     *      "output state (out)" value.
+     *      Type: {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *      Shape: [batchSize, outputSize]
+     */
+    QUANTIZED_LSTM = 95,
+    /**
+     * Executes one of the two referenced subgraphs as determined by a boolean
+     * value.
+     *
+     * The inputs and outputs of the two referenced subgraphs must agree with the
+     * signature of this operation. That is, if the operation has (3 + n) inputs
+     * and m outputs, both subgraphs must have n inputs and m outputs with the same
+     * types, ranks, dimensions, scales,
+     * zeroPoints, and extraParams as the corresponding operation
+     * inputs and outputs.
+     * All of the operands mentioned must have fully specified dimensions.
+     *
+     * Inputs:
+     * * 0: A value of type {@link OperandType::TENSOR_BOOL8} and shape [1]
+     *      that determines which of the two referenced subgraphs to execute.
+     *      The operand must have fully specified dimensions.
+     * * 1: A {@link OperandType::SUBGRAPH} reference to the subgraph to be
+     *      executed if the condition is true.
+     * * 2: A {@link OperandType::SUBGRAPH} reference to the subgraph to be
+     *      executed if the condition is false.
+     * * 3 ~ (n + 2): Inputs to be passed to the subgraph selected for execution.
+     *
+     * Outputs:
+     * * 0 ~ (m - 1): Outputs produced by the selected subgraph.
+     */
+    IF = 96,
+    /**
+     * Executes the body subgraph until the condition subgraph outputs false.
+     *
+     * The inputs to this operation are the condition subgraph, the body subgraph,
+     * and operand values for the first iteration of the loop. The values are
+     * implicitly split into three groups of input-output, state-only, and
+     * input-only values, as described below.
+     *
+     * The outputs of this operation are the final values of input-output
+     * operands.
+     *
+     * Both the condition and body subgraph receive (m + k + n) inputs.
+     * * The first m (m >= 1) inputs are input-output operands. For the first
+     *   iteration, these are initialized from the corresponding inputs of the
+     *   WHILE operation. In subsequent iterations, their values come from the
+     *   corresponding outputs of the body subgraph produced during the previous
+     *   iteration.
+     * * The next k (k >= 0) inputs are state-only operands. They are similar to
+     *   the input-output operands, except that their values are no longer
+     *   available after the loop terminates.
+     * * The last n (n >= 0) inputs are input-only operands. Their values come
+     *   from the corresponding inputs of the WHILE operation.
+     *
+     * The body subgraph produces (m + k) outputs.
+     * * The first m outputs are input-output operands. They become the outputs
+     *   of the WHILE operation when a termination condition is reached.
+     * * The last k outputs are state-only operands. Their values are no longer
+     *   available after the loop terminates.
+     *
+     * The numbers m, k, and n are inferred by the driver as follows:
+     *     m = (WHILE operation output count)
+     *     k = (body subgraph output count) - m
+     *     n = (body subgraph input count) - m - k
+     *
+     * The pseudo-code below illustrates the flow of a WHILE operation with
+     * inputs condition, body, initial_input_output, initial_state, input_only
+     * (m = 1, k = 1, n = 1):
+     *
+     *     input_output = initial_input_output
+     *     state = initial_state
+     *     while condition(input_output, state, input_only):
+     *         input_output, state = body(input_output, state, input_only)
+     *     return input_output
+     *
+     * Inputs:
+     * * 0: A {@link OperandType::SUBGRAPH} reference to the condition
+     *      subgraph. The subgraph must have (m + k + n) inputs with
+     *      the same types, ranks, dimensions,
+     *      scales, zeroPoints, and extraParams as the
+     *      corresponding inputs of the WHILE operation and exactly one output
+     *      of {@link OperandType::TENSOR_BOOL8} and shape [1].
+     *      All of the operands mentioned must have fully specified dimensions.
+     * * 1: A {@link OperandType::SUBGRAPH} reference to the body subgraph.
+     *      The subgraph must have (m + k + n) inputs and (m + k) outputs with
+     *      the same types, ranks, dimensions,
+     *      scales, zeroPoints, and extraParams as the
+     *      corresponding inputs and outputs of the WHILE operation.
+     *      All of the operands mentioned must have fully specified dimensions.
+     * * (m inputs): Initial values for input-output operands.
+     * * (k inputs): Initial values for state-only operands.
+     * * (n inputs): Values for input-only operands.
+     *
+     * Outputs:
+     * * 0 ~ (m - 1): Outputs produced by the loop.
+     */
+    WHILE = 97,
+    /**
+     * Computes exponential linear activation on the input tensor element-wise.
+     *
+     * The output is calculated using the following formula:
+     *
+     *     ELU(x) = max(0, x) + min(0, alpha * (exp(x) - 1))
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input. May be zero-sized.
+     * * 1: A scalar, specifying the alpha parameter.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT16},
+     *      the alpha value must be of {@link OperandType::FLOAT16}.
+     *      For input tensor of {@link OperandType::TENSOR_FLOAT32},
+     *      the alpha value must be of {@link OperandType::FLOAT32}.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape and type as input0.
+     */
+    ELU = 98,
+    /**
+     * Computes hard-swish activation on the input tensor element-wise.
+     *
+     * Hard swish activation is introduced in
+     * https://arxiv.org/pdf/1905.02244.pdf
+     *
+     * The output is calculated using the following formula:
+     *
+     *     h-swish(x) = x * max(0, min(6, (x + 3))) / 6
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A tensor, specifying the input. May be zero-sized.
+     *
+     * Outputs:
+     * * 0: The output tensor of same shape and type as input0.
+     *      Scale and zero point of this tensor may be different from the input
+     *      tensor's parameters.
+     */
+    HARD_SWISH = 99,
+    /**
+     * Creates a tensor filled with a scalar value.
+     *
+     * Supported output tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: A 1-D tensor, specifying the desired output tensor shape.
+     * * 1: A scalar, specifying the value to fill the output tensors with.
+     *      For output tensor of {@link OperandType::TENSOR_FLOAT16},
+     *      the scalar must be of {@link OperandType::FLOAT16}.
+     *      For output tensor of {@link OperandType::TENSOR_FLOAT32},
+     *      the scalar must be of {@link OperandType::FLOAT32}.
+     *      For output tensor of {@link OperandType::TENSOR_INT32},
+     *      the scalar must be of {@link OperandType::INT32}.
+     *
+     * Outputs:
+     * * 0: The output tensor.
+     */
+    FILL = 100,
+    /**
+     * Returns the rank of a tensor.
+     *
+     * The rank of a tensor is the number of dimensions in it. Also known as
+     * "order", "degree", "ndims".
+     *
+     * Supported tensor {@link OperandType}:
+     * * {@link OperandType::TENSOR_FLOAT16}
+     * * {@link OperandType::TENSOR_FLOAT32}
+     * * {@link OperandType::TENSOR_INT32}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT16_SYMM}
+     * * {@link OperandType::TENSOR_BOOL8}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL}
+     * * {@link OperandType::TENSOR_QUANT16_ASYMM}
+     * * {@link OperandType::TENSOR_QUANT8_SYMM}
+     * * {@link OperandType::TENSOR_QUANT8_ASYMM_SIGNED}
+     *
+     * Supported tensor rank: from 1.
+     *
+     * Inputs:
+     * * 0: The input tensor.
+     *
+     * Outputs:
+     * * 0: A scalar of {@link OperandType::INT32}, specifying the rank
+     *      of the input tensor.
+     */
+    RANK = 101,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
new file mode 100644
index 0000000..f90a613
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/OutputShape.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Describes the shape information of an output operand after execution.
+ */
+@VintfStability
+parcelable OutputShape {
+    /**
+     * Dimensions of the operand.
+     */
+    int[] dimensions;
+    /**
+     * Whether the provided buffer size is sufficient for the output.
+     */
+    boolean isSufficient;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl
new file mode 100644
index 0000000..6915c67
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/PerformanceInfo.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Performance information for the reference workload.
+ *
+ * Used by a driver to report its performance characteristics.
+ */
+@VintfStability
+parcelable PerformanceInfo {
+    /**
+     * Ratio of the time taken by the driver to execute the workload compared to the time the CPU
+     * would take for the same workload. A lower number is better.
+     */
+    float execTime;
+    /**
+     * Ratio of the energy used by the driver compared to what the CPU would use for doing the same
+     * workload. A lower number is better.
+     */
+    float powerUsage;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
new file mode 100644
index 0000000..7dbf8e9
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Priority.aidl
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Priority given to a prepared model for execution.
+ */
+@VintfStability
+@Backing(type="int")
+enum Priority {
+    LOW,
+    MEDIUM,
+    HIGH,
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl
new file mode 100644
index 0000000..dc138ba
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Request.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.RequestArgument;
+import android.hardware.neuralnetworks.RequestMemoryPool;
+
+/**
+ * Inputs to be sent to and outputs to be retrieved from a prepared model.
+ *
+ * A Request serves two primary tasks:
+ * 1) Provides the input and output data to be used when executing the model.
+ * 2) Specifies any updates to the input operand metadata that were left unspecified at model
+ *    preparation time.
+ *
+ * An output must not overlap with any other output, with an input, or with an operand of lifetime
+ * CONSTANT_POOL.
+ */
+@VintfStability
+parcelable Request {
+    /**
+     * Input data and information to be used in the execution of a prepared model.
+     *
+     * The index of the input corresponds to the index in Model.main.inputIndexes.
+     *   E.g., input[i] corresponds to Model.main.inputIndexes[i].
+     */
+    RequestArgument[] inputs;
+    /**
+     * Output data and information to be used in the execution of a prepared model.
+     *
+     * The index of the output corresponds to the index in Model.main.outputIndexes.
+     *   E.g., output[i] corresponds to Model.main.outputIndexes[i].
+     */
+    RequestArgument[] outputs;
+    /**
+     * A collection of memory pools containing operand data for both the inputs and the outputs to a
+     * model.
+     */
+    RequestMemoryPool[] pools;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl
new file mode 100644
index 0000000..8dc9252
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestArgument.aidl
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.DataLocation;
+
+/**
+ * Metadata information specifying the location of the input or output data and any updates to the
+ * input or output operand.
+ */
+@VintfStability
+parcelable RequestArgument {
+    /**
+     * If true, the argument does not have a value. This can be used for operations that take
+     * optional arguments. If true, the fields of location are set to 0 and the dimensions vector is
+     * left empty.
+     */
+    boolean hasNoValue;
+    /**
+     * The location within one of the memory pools passed in the Request.
+     */
+    DataLocation location;
+    /**
+     * Updated dimension information.
+     *
+     * If dimensions.size() > 0, dimension information was provided along with the argument. This
+     * can be the case for models that accept inputs of varying size. This can't change the rank,
+     * just the value of the dimensions that were unspecified in the model. If dimensions.size() >
+     * 0, then all dimensions must be specified here; and any dimension that was specified in the
+     * model must have the same value here.
+     *
+     * If the dimensions in the model are not fully specified, then they must be fully specified
+     * here, unless hasNoValue is set to true. If the dimensions in the model are fully specified,
+     * then either dimensions.size() may be 0, or the dimensions in the model must be identical to
+     * the dimensions here.
+     */
+    int[] dimensions;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
new file mode 100644
index 0000000..faca2fe
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/RequestMemoryPool.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.Memory;
+
+/**
+ * A memory pool.
+ */
+@VintfStability
+union RequestMemoryPool {
+    /**
+     * Specifies a client-managed shared memory pool.
+     */
+    Memory pool;
+    /**
+     * Specifies a driver-managed buffer. It is the token returned from IDevice::allocate, and is
+     * specific to the IDevice object.
+     */
+    int token;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl
new file mode 100644
index 0000000..2e9c450
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Subgraph.aidl
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+import android.hardware.neuralnetworks.Operand;
+import android.hardware.neuralnetworks.Operation;
+
+/**
+ * An excerpt of the execution graph.
+ */
+@VintfStability
+parcelable Subgraph {
+    /**
+     * All operands included in the subgraph.
+     */
+    Operand[] operands;
+    /**
+     * All operations included in the subgraph.
+     *
+     * The operations are sorted into execution order. Every operand with lifetime SUBGRAPH_OUTPUT
+     * or TEMPORARY_VARIABLE must be written before it is read.
+     */
+    Operation[] operations;
+    /**
+     * Input indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    int[] inputIndexes;
+    /**
+     * Output indexes of the subgraph. There must be at least one.
+     *
+     * Each value corresponds to the index of the operand in "operands".
+     */
+    int[] outputIndexes;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
new file mode 100644
index 0000000..eb47df0
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Parameters for TENSOR_QUANT8_SYMM_PER_CHANNEL operand.
+ */
+@VintfStability
+parcelable SymmPerChannelQuantParams {
+    /**
+     * Array of scaling values for each channel. Each value must be greater than zero.
+     */
+    float[] scales;
+    /**
+     * Index of the channel dimension
+     */
+    int channelDim;
+}
diff --git a/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
new file mode 100644
index 0000000..5225096
--- /dev/null
+++ b/neuralnetworks/aidl/android/hardware/neuralnetworks/Timing.aidl
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ */
+
+package android.hardware.neuralnetworks;
+
+/**
+ * Timing information measured during execution. Each time is a duration from the beginning of some
+ * task to the end of that task, including time when that task is not active (for example, preempted
+ * by some other task, or waiting for some resource to become available).
+ *
+ * Times are measured in nanoseconds. When a time is not available, it must be reported as -1.
+ */
+@VintfStability
+parcelable Timing {
+    /**
+     * Execution time on device (not driver, which runs on host processor).
+     */
+    long timeOnDeviceNs;
+    /**
+     * Execution time in driver (including time on device).
+     */
+    long timeInDriverNs;
+}
diff --git a/neuralnetworks/aidl/utils/Android.bp b/neuralnetworks/aidl/utils/Android.bp
new file mode 100644
index 0000000..0ccc711
--- /dev/null
+++ b/neuralnetworks/aidl/utils/Android.bp
@@ -0,0 +1,83 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_aidl",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal/aidl/"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "android.hardware.graphics.common-V2-ndk_platform",
+        "libaidlcommonsupport",
+        "libarect",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks-V1-ndk_platform",
+        "libbinder_ndk",
+        "libhidlbase",
+        "libnativewindow",
+    ],
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_aidl_test",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: [
+        "test/*.cpp",
+    ],
+    static_libs: [
+        "android.hardware.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.neuralnetworks-V1-ndk_platform",
+        "libaidlcommonsupport",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_aidl",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "libbase",
+        "libbinder_ndk",
+        "libcutils",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    cflags: [
+        /* GMOCK defines functions for printing all MOCK_DEVICE arguments and
+         * MockDevice contains a string pointer which triggers a warning in the
+         * base logging library. */
+        "-Wno-user-defined-warnings",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/aidl/utils/OWNERS b/neuralnetworks/aidl/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/aidl/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h
new file mode 100644
index 0000000..46190c4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Buffer.h
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_H
+
+#include <aidl/android/hardware/neuralnetworks/IBuffer.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <memory>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IBuffer to  nn::IBuffer.
+class Buffer final : public nn::IBuffer {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Buffer>> create(
+            std::shared_ptr<aidl_hal::IBuffer> buffer, nn::Request::MemoryDomainToken token);
+
+    Buffer(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBuffer> buffer,
+           nn::Request::MemoryDomainToken token);
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
+                                     const nn::Dimensions& dimensions) const override;
+
+  private:
+    const std::shared_ptr<aidl_hal::IBuffer> kBuffer;
+    const nn::Request::MemoryDomainToken kToken;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BUFFER_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
new file mode 100644
index 0000000..0cc78d4
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Burst.h
@@ -0,0 +1,121 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/IBurst.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <atomic>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <unordered_map>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IBurst to nn::IBurst.
+class Burst final : public nn::IBurst, public std::enable_shared_from_this<Burst> {
+    struct PrivateConstructorTag {};
+
+  public:
+    /**
+     * Thread-safe, self-cleaning cache that relates an nn::Memory object to a unique int64_t
+     * identifier.
+     */
+    class MemoryCache : public std::enable_shared_from_this<MemoryCache> {
+      public:
+        using Task = std::function<void()>;
+        using Cleanup = ::android::base::ScopeGuard<Task>;
+        using SharedCleanup = std::shared_ptr<const Cleanup>;
+        using WeakCleanup = std::weak_ptr<const Cleanup>;
+
+        explicit MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst);
+
+        /**
+         * Get or cache a memory object in the MemoryCache object.
+         *
+         * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+         * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+         *     "hold" object which preserves the cache as long as the hold object is alive.
+         */
+        std::pair<int64_t, SharedCleanup> getOrCacheMemory(const nn::SharedMemory& memory);
+
+        /**
+         * Get a cached memory object in the MemoryCache object if it exists, otherwise
+         * std::nullopt.
+         *
+         * @param memory Memory object to be cached while the returned `SharedCleanup` is alive.
+         * @return A pair of (1) a unique identifier for the cache entry and (2) a ref-counted
+         *     "hold" object which preserves the cache as long as the hold object is alive. IF the
+         *     cache entry is not present, std::nullopt is returned instead.
+         */
+        std::optional<std::pair<int64_t, SharedCleanup>> getMemoryIfAvailable(
+                const nn::SharedMemory& memory);
+
+      private:
+        void tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier);
+
+        const std::shared_ptr<aidl_hal::IBurst> kBurst;
+        std::mutex mMutex;
+        int64_t mUnusedIdentifier GUARDED_BY(mMutex) = 0;
+        std::unordered_map<nn::SharedMemory, std::pair<int64_t, WeakCleanup>> mCache
+                GUARDED_BY(mMutex);
+    };
+
+    static nn::GeneralResult<std::shared_ptr<const Burst>> create(
+            std::shared_ptr<aidl_hal::IBurst> burst);
+
+    Burst(PrivateConstructorTag tag, std::shared_ptr<aidl_hal::IBurst> burst);
+
+    // See IBurst::cacheMemory for information.
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+    // See IBurst::execute for information.
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    // See IBurst::createReusableExecution for information.
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const aidl_hal::Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+            bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
+  private:
+    mutable std::atomic_flag mExecutionInFlight = ATOMIC_FLAG_INIT;
+    const std::shared_ptr<aidl_hal::IBurst> kBurst;
+    const std::shared_ptr<MemoryCache> kMemoryCache;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_BURST_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
new file mode 100644
index 0000000..8651912
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Callbacks.h
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_CALLBACKS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_CALLBACKS_H
+
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModelCallback.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/TransferValue.h>
+#include <nnapi/hal/aidl/ProtectCallback.h>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// An AIDL callback class to receive the results of IDevice::prepareModel* asynchronously.
+class PreparedModelCallback final : public BnPreparedModelCallback,
+                                    public hal::utils::IProtectedCallback {
+  public:
+    using Data = nn::GeneralResult<nn::SharedPreparedModel>;
+
+    ndk::ScopedAStatus notify(ErrorStatus status,
+                              const std::shared_ptr<IPreparedModel>& preparedModel) override;
+
+    void notifyAsDeadObject() override;
+
+    Data get();
+
+  private:
+    hal::utils::TransferValue<Data> mData;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_CALLBACKS_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
new file mode 100644
index 0000000..78433a7
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Conversions.h
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_CONVERSIONS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_CONVERSIONS_H
+
+#include <aidl/android/hardware/neuralnetworks/BufferDesc.h>
+#include <aidl/android/hardware/neuralnetworks/BufferRole.h>
+#include <aidl/android/hardware/neuralnetworks/Capabilities.h>
+#include <aidl/android/hardware/neuralnetworks/DataLocation.h>
+#include <aidl/android/hardware/neuralnetworks/DeviceType.h>
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/ExecutionPreference.h>
+#include <aidl/android/hardware/neuralnetworks/Extension.h>
+#include <aidl/android/hardware/neuralnetworks/ExtensionNameAndPrefix.h>
+#include <aidl/android/hardware/neuralnetworks/ExtensionOperandTypeInformation.h>
+#include <aidl/android/hardware/neuralnetworks/Memory.h>
+#include <aidl/android/hardware/neuralnetworks/Model.h>
+#include <aidl/android/hardware/neuralnetworks/Operand.h>
+#include <aidl/android/hardware/neuralnetworks/OperandExtraParams.h>
+#include <aidl/android/hardware/neuralnetworks/OperandLifeTime.h>
+#include <aidl/android/hardware/neuralnetworks/OperandPerformance.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/Operation.h>
+#include <aidl/android/hardware/neuralnetworks/OperationType.h>
+#include <aidl/android/hardware/neuralnetworks/OutputShape.h>
+#include <aidl/android/hardware/neuralnetworks/PerformanceInfo.h>
+#include <aidl/android/hardware/neuralnetworks/Priority.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <aidl/android/hardware/neuralnetworks/RequestArgument.h>
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
+#include <aidl/android/hardware/neuralnetworks/Subgraph.h>
+#include <aidl/android/hardware/neuralnetworks/SymmPerChannelQuantParams.h>
+#include <aidl/android/hardware/neuralnetworks/Timing.h>
+
+#include <android/binder_auto_utils.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <vector>
+
+namespace android::nn {
+
+GeneralResult<OperandType> unvalidatedConvert(const aidl_hal::OperandType& operandType);
+GeneralResult<OperationType> unvalidatedConvert(const aidl_hal::OperationType& operationType);
+GeneralResult<DeviceType> unvalidatedConvert(const aidl_hal::DeviceType& deviceType);
+GeneralResult<Priority> unvalidatedConvert(const aidl_hal::Priority& priority);
+GeneralResult<Capabilities> unvalidatedConvert(const aidl_hal::Capabilities& capabilities);
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const aidl_hal::OperandPerformance& operandPerformance);
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const aidl_hal::PerformanceInfo& performanceInfo);
+GeneralResult<DataLocation> unvalidatedConvert(const aidl_hal::DataLocation& location);
+GeneralResult<Operand> unvalidatedConvert(const aidl_hal::Operand& operand);
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const std::optional<aidl_hal::OperandExtraParams>& optionalExtraParams);
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const aidl_hal::OperandLifeTime& operandLifeTime);
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
+        const aidl_hal::SymmPerChannelQuantParams& symmPerChannelQuantParams);
+GeneralResult<Operation> unvalidatedConvert(const aidl_hal::Operation& operation);
+GeneralResult<Model> unvalidatedConvert(const aidl_hal::Model& model);
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const aidl_hal::ExtensionNameAndPrefix& extensionNameAndPrefix);
+GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues);
+GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subgraph);
+GeneralResult<OutputShape> unvalidatedConvert(const aidl_hal::OutputShape& outputShape);
+GeneralResult<MeasureTiming> unvalidatedConvert(bool measureTiming);
+GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory);
+GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing);
+GeneralResult<BufferDesc> unvalidatedConvert(const aidl_hal::BufferDesc& bufferDesc);
+GeneralResult<BufferRole> unvalidatedConvert(const aidl_hal::BufferRole& bufferRole);
+GeneralResult<Request> unvalidatedConvert(const aidl_hal::Request& request);
+GeneralResult<Request::Argument> unvalidatedConvert(
+        const aidl_hal::RequestArgument& requestArgument);
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const aidl_hal::RequestMemoryPool& memoryPool);
+GeneralResult<ErrorStatus> unvalidatedConvert(const aidl_hal::ErrorStatus& errorStatus);
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const aidl_hal::ExecutionPreference& executionPreference);
+GeneralResult<Extension> unvalidatedConvert(const aidl_hal::Extension& extension);
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const aidl_hal::ExtensionOperandTypeInformation& operandTypeInformation);
+GeneralResult<SharedHandle> unvalidatedConvert(const ndk::ScopedFileDescriptor& handle);
+
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+        const std::vector<aidl_hal::Operation>& operations);
+
+GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities);
+GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType);
+GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus);
+GeneralResult<ExecutionPreference> convert(
+        const aidl_hal::ExecutionPreference& executionPreference);
+GeneralResult<SharedMemory> convert(const aidl_hal::Memory& memory);
+GeneralResult<Model> convert(const aidl_hal::Model& model);
+GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType);
+GeneralResult<Priority> convert(const aidl_hal::Priority& priority);
+GeneralResult<Request> convert(const aidl_hal::Request& request);
+GeneralResult<Timing> convert(const aidl_hal::Timing& timing);
+GeneralResult<SharedHandle> convert(const ndk::ScopedFileDescriptor& handle);
+
+GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension);
+GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories);
+GeneralResult<std::vector<OutputShape>> convert(
+        const std::vector<aidl_hal::OutputShape>& outputShapes);
+
+GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec);
+
+}  // namespace android::nn
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+namespace nn = ::android::nn;
+
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& cacheToken);
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole);
+nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory);
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape);
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus);
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType);
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(const nn::Operand::LifeTime& operandLifeTime);
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location);
+nn::GeneralResult<std::optional<OperandExtraParams>> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams);
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand);
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType);
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation);
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph);
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues);
+nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix);
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model);
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority);
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request);
+nn::GeneralResult<RequestArgument> unvalidatedConvert(const nn::Request::Argument& requestArgument);
+nn::GeneralResult<RequestMemoryPool> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool);
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration);
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalTimePoint& optionalTimePoint);
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SyncFence& syncFence);
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SharedHandle& handle);
+
+nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken);
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc);
+nn::GeneralResult<bool> convert(const nn::MeasureTiming& measureTiming);
+nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory);
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus);
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference);
+nn::GeneralResult<Model> convert(const nn::Model& model);
+nn::GeneralResult<Priority> convert(const nn::Priority& priority);
+nn::GeneralResult<Request> convert(const nn::Request& request);
+nn::GeneralResult<Timing> convert(const nn::Timing& timing);
+nn::GeneralResult<int64_t> convert(const nn::OptionalDuration& optionalDuration);
+nn::GeneralResult<int64_t> convert(const nn::OptionalTimePoint& optionalTimePoint);
+
+nn::GeneralResult<std::vector<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles);
+nn::GeneralResult<std::vector<OutputShape>> convert(
+        const std::vector<nn::OutputShape>& outputShapes);
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+        const std::vector<nn::SharedHandle>& handles);
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+        const std::vector<nn::SyncFence>& syncFences);
+
+nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec);
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_CONVERSIONS_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
new file mode 100644
index 0000000..1457646
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Device.h
@@ -0,0 +1,97 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_DEVICE_H
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/aidl/ProtectCallback.h>
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IDevice to nn::IDevice.
+class Device final : public nn::IDevice {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Device>> create(
+            std::string name, std::shared_ptr<aidl_hal::IDevice> device);
+
+    Device(PrivateConstructorTag tag, std::string name, std::string versionString,
+           nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+           nn::Capabilities capabilities, std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+           std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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;
+
+    DeathMonitor* getDeathMonitor() const;
+
+  private:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::DeviceType kDeviceType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+    const std::shared_ptr<aidl_hal::IDevice> kDevice;
+    const DeathHandler kDeathHandler;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_DEVICE_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
new file mode 100644
index 0000000..a77ea98
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Execution.h
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include "PreparedModel.h"
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class Execution final : public nn::IExecution, public std::enable_shared_from_this<Execution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const Execution>> create(
+            std::shared_ptr<const PreparedModel> preparedModel, Request request,
+            hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration);
+
+    Execution(PrivateConstructorTag tag, std::shared_ptr<const PreparedModel> preparedModel,
+              Request request, hal::utils::RequestRelocation relocation, bool measure,
+              int64_t loopTimeoutDuration);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const PreparedModel> kPreparedModel;
+    const Request kRequest;
+    const hal::utils::RequestRelocation kRelocation;
+    const bool kMeasure;
+    const int64_t kLoopTimeoutDuration;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_EXECUTION_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h
new file mode 100644
index 0000000..e66507a
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/InvalidDevice.h
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_auto_utils.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+class InvalidDevice : public BnDevice {
+  public:
+    static std::shared_ptr<InvalidDevice> create();
+
+    InvalidDevice(Capabilities capabilities, const NumberOfCacheFiles& numberOfCacheFiles,
+                  std::vector<Extension> extensions, DeviceType deviceType,
+                  std::string versionString);
+
+    ndk::ScopedAStatus allocate(const BufferDesc& desc,
+                                const std::vector<IPreparedModelParcel>& preparedModels,
+                                const std::vector<BufferRole>& inputRoles,
+                                const std::vector<BufferRole>& outputRoles,
+                                DeviceBuffer* deviceBuffer) override;
+    ndk::ScopedAStatus getCapabilities(Capabilities* capabilities) override;
+    ndk::ScopedAStatus getNumberOfCacheFilesNeeded(NumberOfCacheFiles* numberOfCacheFiles) override;
+    ndk::ScopedAStatus getSupportedExtensions(std::vector<Extension>* extensions) override;
+    ndk::ScopedAStatus getSupportedOperations(const Model& model,
+                                              std::vector<bool>* supportedOperations) override;
+    ndk::ScopedAStatus getType(DeviceType* deviceType) override;
+    ndk::ScopedAStatus getVersionString(std::string* versionString) override;
+    ndk::ScopedAStatus prepareModel(
+            const Model& model, ExecutionPreference preference, Priority priority, int64_t deadline,
+            const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+            const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+            const std::vector<uint8_t>& token,
+            const std::shared_ptr<IPreparedModelCallback>& callback) override;
+    ndk::ScopedAStatus prepareModelFromCache(
+            int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+            const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+            const std::vector<uint8_t>& token,
+            const std::shared_ptr<IPreparedModelCallback>& callback) override;
+
+  private:
+    const Capabilities kCapabilities;
+    const NumberOfCacheFiles kNumberOfCacheFiles;
+    const std::vector<Extension> kExtensions;
+    const DeviceType kDeviceType;
+    const std::string kVersionString;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
new file mode 100644
index 0000000..4035764
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/PreparedModel.h
@@ -0,0 +1,85 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
+
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Class that adapts aidl_hal::IPreparedModel to nn::IPreparedModel.
+class PreparedModel final : public nn::IPreparedModel,
+                            public std::enable_shared_from_this<PreparedModel> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const PreparedModel>> create(
+            std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
+
+    PreparedModel(PrivateConstructorTag tag,
+                  std::shared_ptr<aidl_hal::IPreparedModel> preparedModel);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> executeInternal(
+            const Request& request, bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+            const hal::utils::RequestRelocation& relocation) const;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+    executeFencedInternal(const Request& request,
+                          const std::vector<ndk::ScopedFileDescriptor>& waitFor, bool measure,
+                          int64_t deadline, int64_t loopTimeoutDuration,
+                          int64_t timeoutDurationAfterFence,
+                          const hal::utils::RequestRelocation& relocation) const;
+
+  private:
+    const std::shared_ptr<aidl_hal::IPreparedModel> kPreparedModel;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PREPARED_MODEL_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
new file mode 100644
index 0000000..ab1108c
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/ProtectCallback.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PROTECT_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PROTECT_CALLBACK_H
+
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <functional>
+#include <mutex>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+// Thread safe class
+class DeathMonitor final {
+  public:
+    static void serviceDied(void* cookie);
+    void serviceDied();
+    // Precondition: `killable` must be non-null.
+    void add(hal::utils::IProtectedCallback* killable) const;
+    // Precondition: `killable` must be non-null.
+    void remove(hal::utils::IProtectedCallback* killable) const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::vector<hal::utils::IProtectedCallback*> mObjects GUARDED_BY(mMutex);
+};
+
+class DeathHandler final {
+  public:
+    static nn::GeneralResult<DeathHandler> create(std::shared_ptr<ndk::ICInterface> 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]] ::android::base::ScopeGuard<Cleanup> protectCallback(
+            hal::utils::IProtectedCallback* killable) const;
+
+    std::shared_ptr<DeathMonitor> getDeathMonitor() const { return kDeathMonitor; }
+
+  private:
+    DeathHandler(std::shared_ptr<ndk::ICInterface> object,
+                 ndk::ScopedAIBinder_DeathRecipient deathRecipient,
+                 std::shared_ptr<DeathMonitor> deathMonitor);
+
+    std::shared_ptr<ndk::ICInterface> kObject;
+    ndk::ScopedAIBinder_DeathRecipient kDeathRecipient;
+    std::shared_ptr<DeathMonitor> kDeathMonitor;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_PROTECT_CALLBACK_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h
new file mode 100644
index 0000000..cb6ff4b
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Service.h
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <string>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+::android::nn::GeneralResult<::android::nn::SharedDevice> getDevice(const std::string& name);
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_SERVICE_H
diff --git a/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
new file mode 100644
index 0000000..316d34f
--- /dev/null
+++ b/neuralnetworks/aidl/utils/include/nnapi/hal/aidl/Utils.h
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
+
+#include "nnapi/hal/aidl/Conversions.h"
+
+#include <android-base/logging.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/HandleError.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+constexpr auto kDefaultPriority = Priority::MEDIUM;
+constexpr auto kVersion = nn::Version::ANDROID_S;
+
+template <typename Type>
+nn::Result<void> validate(const Type& halObject) {
+    const auto maybeCanonical = nn::convert(halObject);
+    if (!maybeCanonical.has_value()) {
+        return nn::error() << maybeCanonical.error().message;
+    }
+    return {};
+}
+
+template <typename Type>
+bool valid(const Type& halObject) {
+    const auto result = utils::validate(halObject);
+    if (!result.has_value()) {
+        LOG(ERROR) << result.error();
+    }
+    return result.has_value();
+}
+
+template <typename Type>
+nn::GeneralResult<void> compliantVersion(const Type& canonical) {
+    const auto version = NN_TRY(::android::hardware::neuralnetworks::utils::makeGeneralFailure(
+            nn::validate(canonical)));
+    if (version > kVersion) {
+        return NN_ERROR() << "Insufficient version: " << version << " vs required " << kVersion;
+    }
+    return {};
+}
+
+template <typename Type>
+auto convertFromNonCanonical(const Type& nonCanonicalObject)
+        -> decltype(convert(nn::convert(nonCanonicalObject).value())) {
+    return convert(NN_TRY(nn::convert(nonCanonicalObject)));
+}
+
+nn::GeneralResult<Memory> clone(const Memory& memory);
+nn::GeneralResult<Request> clone(const Request& request);
+nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool);
+nn::GeneralResult<Model> clone(const Model& model);
+
+nn::GeneralResult<void> handleTransportError(const ndk::ScopedAStatus& ret);
+
+#define HANDLE_ASTATUS(ret)                                            \
+    for (const auto status = handleTransportError(ret); !status.ok();) \
+    return NN_ERROR(status.error().code) << status.error().message << ": "
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_H
diff --git a/neuralnetworks/aidl/utils/src/Assertions.cpp b/neuralnetworks/aidl/utils/src/Assertions.cpp
new file mode 100644
index 0000000..0e88091
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Assertions.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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.
+ */
+
+#include <aidl/android/hardware/neuralnetworks/DeviceType.h>
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/ExecutionPreference.h>
+#include <aidl/android/hardware/neuralnetworks/FusedActivationFunc.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <aidl/android/hardware/neuralnetworks/OperandLifeTime.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/OperationType.h>
+#include <aidl/android/hardware/neuralnetworks/Priority.h>
+
+#include <ControlFlow.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Types.h>
+#include <type_traits>
+
+namespace {
+
+#define COMPARE_ENUMS_TYPES(lhsType, rhsType)                                                   \
+    static_assert(                                                                              \
+            std::is_same_v<                                                                     \
+                    std::underlying_type_t<::aidl::android::hardware::neuralnetworks::lhsType>, \
+                    std::underlying_type_t<::android::nn::rhsType>>,                            \
+            "::aidl::android::hardware::neuralnetworks::" #lhsType                              \
+            " does not have the same underlying type as ::android::nn::" #rhsType)
+
+COMPARE_ENUMS_TYPES(OperandType, OperandType);
+COMPARE_ENUMS_TYPES(OperationType, OperationType);
+COMPARE_ENUMS_TYPES(Priority, Priority);
+COMPARE_ENUMS_TYPES(OperandLifeTime, Operand::LifeTime);
+COMPARE_ENUMS_TYPES(ErrorStatus, ErrorStatus);
+
+#undef COMPARE_ENUMS_TYPES
+
+#define COMPARE_ENUMS_FULL(lhsSymbol, rhsSymbol, lhsType, rhsType)                               \
+    static_assert(                                                                               \
+            static_cast<                                                                         \
+                    std::underlying_type_t<::aidl::android::hardware::neuralnetworks::lhsType>>( \
+                    ::aidl::android::hardware::neuralnetworks::lhsType::lhsSymbol) ==            \
+                    static_cast<std::underlying_type_t<::android::nn::rhsType>>(                 \
+                            ::android::nn::rhsType::rhsSymbol),                                  \
+            "::aidl::android::hardware::neuralnetworks::" #lhsType "::" #lhsSymbol               \
+            " does not match ::android::nn::" #rhsType "::" #rhsSymbol)
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, OperandType, OperandType)
+
+COMPARE_ENUMS(FLOAT32);
+COMPARE_ENUMS(INT32);
+COMPARE_ENUMS(UINT32);
+COMPARE_ENUMS(TENSOR_FLOAT32);
+COMPARE_ENUMS(TENSOR_INT32);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM);
+COMPARE_ENUMS(BOOL);
+COMPARE_ENUMS(TENSOR_QUANT16_SYMM);
+COMPARE_ENUMS(TENSOR_FLOAT16);
+COMPARE_ENUMS(TENSOR_BOOL8);
+COMPARE_ENUMS(FLOAT16);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM_PER_CHANNEL);
+COMPARE_ENUMS(TENSOR_QUANT16_ASYMM);
+COMPARE_ENUMS(TENSOR_QUANT8_SYMM);
+COMPARE_ENUMS(TENSOR_QUANT8_ASYMM_SIGNED);
+COMPARE_ENUMS(SUBGRAPH);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, OperationType, OperationType)
+
+COMPARE_ENUMS(ADD);
+COMPARE_ENUMS(AVERAGE_POOL_2D);
+COMPARE_ENUMS(CONCATENATION);
+COMPARE_ENUMS(CONV_2D);
+COMPARE_ENUMS(DEPTHWISE_CONV_2D);
+COMPARE_ENUMS(DEPTH_TO_SPACE);
+COMPARE_ENUMS(DEQUANTIZE);
+COMPARE_ENUMS(EMBEDDING_LOOKUP);
+COMPARE_ENUMS(FLOOR);
+COMPARE_ENUMS(FULLY_CONNECTED);
+COMPARE_ENUMS(HASHTABLE_LOOKUP);
+COMPARE_ENUMS(L2_NORMALIZATION);
+COMPARE_ENUMS(L2_POOL_2D);
+COMPARE_ENUMS(LOCAL_RESPONSE_NORMALIZATION);
+COMPARE_ENUMS(LOGISTIC);
+COMPARE_ENUMS(LSH_PROJECTION);
+COMPARE_ENUMS(LSTM);
+COMPARE_ENUMS(MAX_POOL_2D);
+COMPARE_ENUMS(MUL);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+COMPARE_ENUMS(RESHAPE);
+COMPARE_ENUMS(RESIZE_BILINEAR);
+COMPARE_ENUMS(RNN);
+COMPARE_ENUMS(SOFTMAX);
+COMPARE_ENUMS(SPACE_TO_DEPTH);
+COMPARE_ENUMS(SVDF);
+COMPARE_ENUMS(TANH);
+COMPARE_ENUMS(BATCH_TO_SPACE_ND);
+COMPARE_ENUMS(DIV);
+COMPARE_ENUMS(MEAN);
+COMPARE_ENUMS(PAD);
+COMPARE_ENUMS(SPACE_TO_BATCH_ND);
+COMPARE_ENUMS(SQUEEZE);
+COMPARE_ENUMS(STRIDED_SLICE);
+COMPARE_ENUMS(SUB);
+COMPARE_ENUMS(TRANSPOSE);
+COMPARE_ENUMS(ABS);
+COMPARE_ENUMS(ARGMAX);
+COMPARE_ENUMS(ARGMIN);
+COMPARE_ENUMS(AXIS_ALIGNED_BBOX_TRANSFORM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(BIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(BOX_WITH_NMS_LIMIT);
+COMPARE_ENUMS(CAST);
+COMPARE_ENUMS(CHANNEL_SHUFFLE);
+COMPARE_ENUMS(DETECTION_POSTPROCESSING);
+COMPARE_ENUMS(EQUAL);
+COMPARE_ENUMS(EXP);
+COMPARE_ENUMS(EXPAND_DIMS);
+COMPARE_ENUMS(GATHER);
+COMPARE_ENUMS(GENERATE_PROPOSALS);
+COMPARE_ENUMS(GREATER);
+COMPARE_ENUMS(GREATER_EQUAL);
+COMPARE_ENUMS(GROUPED_CONV_2D);
+COMPARE_ENUMS(HEATMAP_MAX_KEYPOINT);
+COMPARE_ENUMS(INSTANCE_NORMALIZATION);
+COMPARE_ENUMS(LESS);
+COMPARE_ENUMS(LESS_EQUAL);
+COMPARE_ENUMS(LOG);
+COMPARE_ENUMS(LOGICAL_AND);
+COMPARE_ENUMS(LOGICAL_NOT);
+COMPARE_ENUMS(LOGICAL_OR);
+COMPARE_ENUMS(LOG_SOFTMAX);
+COMPARE_ENUMS(MAXIMUM);
+COMPARE_ENUMS(MINIMUM);
+COMPARE_ENUMS(NEG);
+COMPARE_ENUMS(NOT_EQUAL);
+COMPARE_ENUMS(PAD_V2);
+COMPARE_ENUMS(POW);
+COMPARE_ENUMS(PRELU);
+COMPARE_ENUMS(QUANTIZE);
+COMPARE_ENUMS(QUANTIZED_16BIT_LSTM);
+COMPARE_ENUMS(RANDOM_MULTINOMIAL);
+COMPARE_ENUMS(REDUCE_ALL);
+COMPARE_ENUMS(REDUCE_ANY);
+COMPARE_ENUMS(REDUCE_MAX);
+COMPARE_ENUMS(REDUCE_MIN);
+COMPARE_ENUMS(REDUCE_PROD);
+COMPARE_ENUMS(REDUCE_SUM);
+COMPARE_ENUMS(ROI_ALIGN);
+COMPARE_ENUMS(ROI_POOLING);
+COMPARE_ENUMS(RSQRT);
+COMPARE_ENUMS(SELECT);
+COMPARE_ENUMS(SIN);
+COMPARE_ENUMS(SLICE);
+COMPARE_ENUMS(SPLIT);
+COMPARE_ENUMS(SQRT);
+COMPARE_ENUMS(TILE);
+COMPARE_ENUMS(TOPK_V2);
+COMPARE_ENUMS(TRANSPOSE_CONV_2D);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_LSTM);
+COMPARE_ENUMS(UNIDIRECTIONAL_SEQUENCE_RNN);
+COMPARE_ENUMS(RESIZE_NEAREST_NEIGHBOR);
+COMPARE_ENUMS(QUANTIZED_LSTM);
+COMPARE_ENUMS(IF);
+COMPARE_ENUMS(WHILE);
+COMPARE_ENUMS(ELU);
+COMPARE_ENUMS(HARD_SWISH);
+COMPARE_ENUMS(FILL);
+COMPARE_ENUMS(RANK);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, Priority, Priority)
+
+COMPARE_ENUMS(LOW);
+COMPARE_ENUMS(MEDIUM);
+COMPARE_ENUMS(HIGH);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(lhsSymbol, rhsSymbol) \
+    COMPARE_ENUMS_FULL(lhsSymbol, rhsSymbol, OperandLifeTime, Operand::LifeTime)
+
+COMPARE_ENUMS(TEMPORARY_VARIABLE, TEMPORARY_VARIABLE);
+COMPARE_ENUMS(SUBGRAPH_INPUT, SUBGRAPH_INPUT);
+COMPARE_ENUMS(SUBGRAPH_OUTPUT, SUBGRAPH_OUTPUT);
+COMPARE_ENUMS(CONSTANT_COPY, CONSTANT_COPY);
+COMPARE_ENUMS(CONSTANT_POOL, CONSTANT_REFERENCE);
+COMPARE_ENUMS(NO_VALUE, NO_VALUE);
+COMPARE_ENUMS(SUBGRAPH, SUBGRAPH);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, ErrorStatus, ErrorStatus)
+
+COMPARE_ENUMS(NONE);
+COMPARE_ENUMS(DEVICE_UNAVAILABLE);
+COMPARE_ENUMS(GENERAL_FAILURE);
+COMPARE_ENUMS(OUTPUT_INSUFFICIENT_SIZE);
+COMPARE_ENUMS(INVALID_ARGUMENT);
+COMPARE_ENUMS(MISSED_DEADLINE_TRANSIENT);
+COMPARE_ENUMS(MISSED_DEADLINE_PERSISTENT);
+COMPARE_ENUMS(RESOURCE_EXHAUSTED_TRANSIENT);
+COMPARE_ENUMS(RESOURCE_EXHAUSTED_PERSISTENT);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) \
+    COMPARE_ENUMS_FULL(symbol, symbol, ExecutionPreference, ExecutionPreference)
+
+COMPARE_ENUMS(LOW_POWER);
+COMPARE_ENUMS(FAST_SINGLE_ANSWER);
+COMPARE_ENUMS(SUSTAINED_SPEED);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) COMPARE_ENUMS_FULL(symbol, symbol, DeviceType, DeviceType)
+
+COMPARE_ENUMS(OTHER);
+COMPARE_ENUMS(CPU);
+COMPARE_ENUMS(GPU);
+COMPARE_ENUMS(ACCELERATOR);
+
+#undef COMPARE_ENUMS
+
+#define COMPARE_ENUMS(symbol) \
+    COMPARE_ENUMS_FULL(symbol, symbol, FusedActivationFunc, FusedActivationFunc)
+
+COMPARE_ENUMS(NONE);
+COMPARE_ENUMS(RELU);
+COMPARE_ENUMS(RELU1);
+COMPARE_ENUMS(RELU6);
+
+#undef COMPARE_ENUMS
+
+#undef COMPARE_ENUMS_FULL
+
+#define COMPARE_CONSTANTS(halSymbol, canonicalSymbol)                     \
+    static_assert(::aidl::android::hardware::neuralnetworks::halSymbol == \
+                  ::android::nn::canonicalSymbol);
+
+COMPARE_CONSTANTS(IDevice::BYTE_SIZE_OF_CACHE_TOKEN, kByteSizeOfCacheToken);
+COMPARE_CONSTANTS(IDevice::MAX_NUMBER_OF_CACHE_FILES, kMaxNumberOfCacheFiles);
+COMPARE_CONSTANTS(IDevice::EXTENSION_TYPE_HIGH_BITS_PREFIX, kExtensionPrefixBits - 1);
+COMPARE_CONSTANTS(IDevice::EXTENSION_TYPE_LOW_BITS_TYPE, kExtensionTypeBits);
+COMPARE_CONSTANTS(IPreparedModel::DEFAULT_LOOP_TIMEOUT_DURATION_NS,
+                  operation_while::kTimeoutNsDefault);
+COMPARE_CONSTANTS(IPreparedModel::MAXIMUM_LOOP_TIMEOUT_DURATION_NS,
+                  operation_while::kTimeoutNsMaximum);
+
+#undef COMPARE_CONSTANTS
+
+}  // anonymous namespace
diff --git a/neuralnetworks/aidl/utils/src/Buffer.cpp b/neuralnetworks/aidl/utils/src/Buffer.cpp
new file mode 100644
index 0000000..c729a68
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Buffer.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#include "Buffer.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+#include "nnapi/hal/aidl/Conversions.h"
+
+#include <memory>
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const Buffer>> Buffer::create(
+        std::shared_ptr<aidl_hal::IBuffer> buffer, nn::Request::MemoryDomainToken token) {
+    if (buffer == nullptr) {
+        return NN_ERROR() << "aidl_hal::utils::Buffer::create must have non-null buffer";
+    }
+    if (token == static_cast<nn::Request::MemoryDomainToken>(0)) {
+        return NN_ERROR() << "aidl_hal::utils::Buffer::create must have non-zero token";
+    }
+
+    return std::make_shared<const Buffer>(PrivateConstructorTag{}, std::move(buffer), token);
+}
+
+Buffer::Buffer(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBuffer> buffer,
+               nn::Request::MemoryDomainToken token)
+    : kBuffer(std::move(buffer)), kToken(token) {
+    CHECK(kBuffer != nullptr);
+    CHECK(kToken != static_cast<nn::Request::MemoryDomainToken>(0));
+}
+
+nn::Request::MemoryDomainToken Buffer::getToken() const {
+    return kToken;
+}
+
+nn::GeneralResult<void> Buffer::copyTo(const nn::SharedMemory& dst) const {
+    const auto aidlDst = NN_TRY(convert(dst));
+
+    const auto ret = kBuffer->copyTo(aidlDst);
+    HANDLE_ASTATUS(ret) << "IBuffer::copyTo failed";
+
+    return {};
+}
+
+nn::GeneralResult<void> Buffer::copyFrom(const nn::SharedMemory& src,
+                                         const nn::Dimensions& dimensions) const {
+    const auto aidlSrc = NN_TRY(convert(src));
+    const auto aidlDimensions = NN_TRY(toSigned(dimensions));
+
+    const auto ret = kBuffer->copyFrom(aidlSrc, aidlDimensions);
+    HANDLE_ASTATUS(ret) << "IBuffer::copyFrom failed";
+
+    return {};
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Burst.cpp b/neuralnetworks/aidl/utils/src/Burst.cpp
new file mode 100644
index 0000000..800ac32
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Burst.cpp
@@ -0,0 +1,324 @@
+/*
+ * 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.
+ */
+
+#include "Burst.h"
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+class BurstExecution final : public nn::IExecution,
+                             public std::enable_shared_from_this<BurstExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    static nn::GeneralResult<std::shared_ptr<const BurstExecution>> create(
+            std::shared_ptr<const Burst> burst, Request request,
+            std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+            hal::utils::RequestRelocation relocation,
+            std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+    BurstExecution(PrivateConstructorTag tag, std::shared_ptr<const Burst> burst, Request request,
+                   std::vector<int64_t> memoryIdentifierTokens, bool measure,
+                   int64_t loopTimeoutDuration, hal::utils::RequestRelocation relocation,
+                   std::vector<Burst::OptionalCacheHold> cacheHolds);
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    const std::shared_ptr<const Burst> kBurst;
+    const Request kRequest;
+    const std::vector<int64_t> kMemoryIdentifierTokens;
+    const bool kMeasure;
+    const int64_t kLoopTimeoutDuration;
+    const hal::utils::RequestRelocation kRelocation;
+    const std::vector<Burst::OptionalCacheHold> kCacheHolds;
+};
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+        const std::vector<OutputShape>& outputShapes, const Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+}  // namespace
+
+Burst::MemoryCache::MemoryCache(std::shared_ptr<aidl_hal::IBurst> burst)
+    : kBurst(std::move(burst)) {}
+
+std::pair<int64_t, Burst::MemoryCache::SharedCleanup> Burst::MemoryCache::getOrCacheMemory(
+        const nn::SharedMemory& memory) {
+    std::lock_guard lock(mMutex);
+
+    // Get the cache payload or create it (with default values) if it does not exist.
+    auto& cachedPayload = mCache[memory];
+    {
+        const auto& [identifier, maybeCleaner] = cachedPayload;
+        // If cache payload already exists, reuse it.
+        if (auto cleaner = maybeCleaner.lock()) {
+            return std::make_pair(identifier, std::move(cleaner));
+        }
+    }
+
+    // If the code reaches this point, the cached payload either did not exist or expired prior to
+    // this call.
+
+    // Allocate a new identifier.
+    CHECK_LT(mUnusedIdentifier, std::numeric_limits<int64_t>::max());
+    const int64_t identifier = mUnusedIdentifier++;
+
+    // Create reference-counted self-cleaning cache object.
+    auto self = weak_from_this();
+    Task cleanup = [memory, identifier, maybeMemoryCache = std::move(self)] {
+        if (const auto memoryCache = maybeMemoryCache.lock()) {
+            memoryCache->tryFreeMemory(memory, identifier);
+        }
+    };
+    auto cleaner = std::make_shared<const Cleanup>(std::move(cleanup));
+
+    // Store the result in the cache and return it.
+    auto result = std::make_pair(identifier, std::move(cleaner));
+    cachedPayload = result;
+    return result;
+}
+
+std::optional<std::pair<int64_t, Burst::MemoryCache::SharedCleanup>>
+Burst::MemoryCache::getMemoryIfAvailable(const nn::SharedMemory& memory) {
+    std::lock_guard lock(mMutex);
+
+    // Get the existing cached entry if it exists.
+    const auto iter = mCache.find(memory);
+    if (iter != mCache.end()) {
+        const auto& [identifier, maybeCleaner] = iter->second;
+        if (auto cleaner = maybeCleaner.lock()) {
+            return std::make_pair(identifier, std::move(cleaner));
+        }
+    }
+
+    // If the code reaches this point, the cached payload did not exist or was actively being
+    // deleted.
+    return std::nullopt;
+}
+
+void Burst::MemoryCache::tryFreeMemory(const nn::SharedMemory& memory, int64_t identifier) {
+    {
+        std::lock_guard guard(mMutex);
+        // Remove the cached memory and payload if it is present but expired. Note that it may not
+        // be present or may not be expired because another thread may have removed or cached the
+        // same memory object before the current thread locked mMutex in tryFreeMemory.
+        const auto iter = mCache.find(memory);
+        if (iter != mCache.end()) {
+            if (std::get<WeakCleanup>(iter->second).expired()) {
+                mCache.erase(iter);
+            }
+        }
+    }
+    kBurst->releaseMemoryResource(identifier);
+}
+
+nn::GeneralResult<std::shared_ptr<const Burst>> Burst::create(
+        std::shared_ptr<aidl_hal::IBurst> burst) {
+    if (burst == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "aidl_hal::utils::Burst::create must have non-null burst";
+    }
+
+    return std::make_shared<const Burst>(PrivateConstructorTag{}, std::move(burst));
+}
+
+Burst::Burst(PrivateConstructorTag /*tag*/, std::shared_ptr<aidl_hal::IBurst> burst)
+    : kBurst(std::move(burst)), kMemoryCache(std::make_shared<MemoryCache>(kBurst)) {
+    CHECK(kBurst != nullptr);
+}
+
+Burst::OptionalCacheHold Burst::cacheMemory(const nn::SharedMemory& memory) const {
+    auto [identifier, hold] = kMemoryCache->getOrCacheMemory(memory);
+    return hold;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto aidlLoopTimeoutDuration =
+            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+
+    std::vector<int64_t> memoryIdentifierTokens;
+    std::vector<OptionalCacheHold> holds;
+    memoryIdentifierTokens.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
+            if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
+                auto& [identifier, hold] = *cached;
+                memoryIdentifierTokens.push_back(identifier);
+                holds.push_back(std::move(hold));
+                continue;
+            }
+        }
+        memoryIdentifierTokens.push_back(-1);
+    }
+    CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+    return executeInternal(aidlRequest, memoryIdentifierTokens, aidlMeasure, aidlDeadline,
+                           aidlLoopTimeoutDuration, relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Burst::executeInternal(
+        const Request& request, const std::vector<int64_t>& memoryIdentifierTokens, bool measure,
+        int64_t deadline, int64_t loopTimeoutDuration,
+        const hal::utils::RequestRelocation& relocation) const {
+    // Ensure that at most one execution is in flight at any given time.
+    const bool alreadyInFlight = mExecutionInFlight.test_and_set();
+    if (alreadyInFlight) {
+        return NN_ERROR() << "IBurst already has an execution in flight";
+    }
+    const auto guard = ::android::base::make_scope_guard([this] { mExecutionInFlight.clear(); });
+
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    ExecutionResult executionResult;
+    const auto ret = kBurst->executeSynchronously(request, memoryIdentifierTokens, measure,
+                                                  deadline, loopTimeoutDuration, &executionResult);
+    HANDLE_ASTATUS(ret) << "execute failed";
+    if (!executionResult.outputSufficientSize) {
+        auto canonicalOutputShapes =
+                nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{});
+        return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+               << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+    }
+    auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
+            convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return std::make_pair(std::move(outputShapes), timing);
+}
+
+nn::GeneralResult<nn::SharedExecution> Burst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto aidlRequest = NN_TRY(convert(requestInShared));
+    const auto aidlMeasure = NN_TRY(convert(measure));
+    const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+
+    std::vector<int64_t> memoryIdentifierTokens;
+    std::vector<OptionalCacheHold> holds;
+    memoryIdentifierTokens.reserve(requestInShared.pools.size());
+    holds.reserve(requestInShared.pools.size());
+    for (const auto& memoryPool : requestInShared.pools) {
+        if (const auto* memory = std::get_if<nn::SharedMemory>(&memoryPool)) {
+            if (auto cached = kMemoryCache->getMemoryIfAvailable(*memory)) {
+                auto& [identifier, hold] = *cached;
+                memoryIdentifierTokens.push_back(identifier);
+                holds.push_back(std::move(hold));
+                continue;
+            }
+        }
+        memoryIdentifierTokens.push_back(-1);
+    }
+    CHECK_EQ(requestInShared.pools.size(), memoryIdentifierTokens.size());
+
+    return BurstExecution::create(shared_from_this(), std::move(aidlRequest),
+                                  std::move(memoryIdentifierTokens), aidlMeasure,
+                                  aidlLoopTimeoutDuration, std::move(relocation), std::move(holds));
+}
+
+nn::GeneralResult<std::shared_ptr<const BurstExecution>> BurstExecution::create(
+        std::shared_ptr<const Burst> burst, Request request,
+        std::vector<int64_t> memoryIdentifierTokens, bool measure, int64_t loopTimeoutDuration,
+        hal::utils::RequestRelocation relocation,
+        std::vector<Burst::OptionalCacheHold> cacheHolds) {
+    if (burst == nullptr) {
+        return NN_ERROR() << "aidl::utils::BurstExecution::create must have non-null burst";
+    }
+
+    return std::make_shared<const BurstExecution>(
+            PrivateConstructorTag{}, std::move(burst), std::move(request),
+            std::move(memoryIdentifierTokens), measure, loopTimeoutDuration, std::move(relocation),
+            std::move(cacheHolds));
+}
+
+BurstExecution::BurstExecution(PrivateConstructorTag /*tag*/, std::shared_ptr<const Burst> burst,
+                               Request request, std::vector<int64_t> memoryIdentifierTokens,
+                               bool measure, int64_t loopTimeoutDuration,
+                               hal::utils::RequestRelocation relocation,
+                               std::vector<Burst::OptionalCacheHold> cacheHolds)
+    : kBurst(std::move(burst)),
+      kRequest(std::move(request)),
+      kMemoryIdentifierTokens(std::move(memoryIdentifierTokens)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(loopTimeoutDuration),
+      kRelocation(std::move(relocation)),
+      kCacheHolds(std::move(cacheHolds)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> BurstExecution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kBurst->executeInternal(kRequest, kMemoryIdentifierTokens, kMeasure, aidlDeadline,
+                                   kLoopTimeoutDuration, kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+BurstExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+           << "IExecution::computeFenced is not supported on burst object";
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Callbacks.cpp b/neuralnetworks/aidl/utils/src/Callbacks.cpp
new file mode 100644
index 0000000..8055665
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Callbacks.cpp
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#include "Callbacks.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+// Converts the results of IDevice::prepareModel* to the NN canonical format. On success, this
+// function returns with a non-null nn::SharedPreparedModel with a feature level of
+// nn::Version::ANDROID_S. On failure, this function returns with the appropriate nn::GeneralError.
+nn::GeneralResult<nn::SharedPreparedModel> prepareModelCallback(
+        ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
+    HANDLE_HAL_STATUS(status) << "model preparation failed with " << toString(status);
+    return NN_TRY(PreparedModel::create(preparedModel));
+}
+
+}  // namespace
+
+ndk::ScopedAStatus PreparedModelCallback::notify(
+        ErrorStatus status, const std::shared_ptr<IPreparedModel>& preparedModel) {
+    mData.put(prepareModelCallback(status, preparedModel));
+    return ndk::ScopedAStatus::ok();
+}
+
+void PreparedModelCallback::notifyAsDeadObject() {
+    mData.put(NN_ERROR(nn::ErrorStatus::DEAD_OBJECT) << "Dead object");
+}
+
+PreparedModelCallback::Data PreparedModelCallback::get() {
+    return mData.take();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Conversions.cpp b/neuralnetworks/aidl/utils/src/Conversions.cpp
new file mode 100644
index 0000000..0517730
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Conversions.cpp
@@ -0,0 +1,1049 @@
+/*
+ * 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.
+ */
+
+#include "Conversions.h"
+
+#include <aidl/android/hardware/common/Ashmem.h>
+#include <aidl/android/hardware/common/MappableFile.h>
+#include <aidl/android/hardware/common/NativeHandle.h>
+#include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
+#include <aidlcommonsupport/NativeHandle.h>
+#include <android-base/logging.h>
+#include <android-base/mapped_file.h>
+#include <android-base/unique_fd.h>
+#include <android/binder_auto_utils.h>
+#include <android/hardware_buffer.h>
+#include <cutils/native_handle.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/OperationTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+#include <vndk/hardware_buffer.h>
+
+#include <algorithm>
+#include <chrono>
+#include <functional>
+#include <iterator>
+#include <limits>
+#include <type_traits>
+#include <utility>
+
+#include "Utils.h"
+
+#define VERIFY_NON_NEGATIVE(value) \
+    while (UNLIKELY(value < 0)) return NN_ERROR()
+
+#define VERIFY_LE_INT32_MAX(value) \
+    while (UNLIKELY(value > std::numeric_limits<int32_t>::max())) return NN_ERROR()
+
+namespace {
+template <typename Type>
+constexpr std::underlying_type_t<Type> underlyingType(Type value) {
+    return static_cast<std::underlying_type_t<Type>>(value);
+}
+
+constexpr int64_t kNoTiming = -1;
+
+}  // namespace
+
+namespace android::nn {
+namespace {
+
+using ::aidl::android::hardware::common::NativeHandle;
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(nn::unvalidatedConvert(argument)));
+    }
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& halObject) {
+    auto canonical = NN_TRY(nn::unvalidatedConvert(halObject));
+    NN_TRY(aidl_hal::utils::compliantVersion(canonical));
+    return canonical;
+}
+
+template <typename Type>
+GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> canonical;
+    canonical.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        canonical.push_back(NN_TRY(validatedConvert(argument)));
+    }
+    return canonical;
+}
+
+struct NativeHandleDeleter {
+    void operator()(native_handle_t* handle) const {
+        if (handle) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    }
+};
+
+using UniqueNativeHandle = std::unique_ptr<native_handle_t, NativeHandleDeleter>;
+
+GeneralResult<UniqueNativeHandle> nativeHandleFromAidlHandle(const NativeHandle& handle) {
+    auto nativeHandle = UniqueNativeHandle(dupFromAidl(handle));
+    if (nativeHandle.get() == nullptr) {
+        return NN_ERROR() << "android::dupFromAidl failed to convert the common::NativeHandle to a "
+                             "native_handle_t";
+    }
+    if (!std::all_of(nativeHandle->data + 0, nativeHandle->data + nativeHandle->numFds,
+                     [](int fd) { return fd >= 0; })) {
+        return NN_ERROR() << "android::dupFromAidl returned an invalid native_handle_t";
+    }
+    return nativeHandle;
+}
+
+}  // anonymous namespace
+
+GeneralResult<OperandType> unvalidatedConvert(const aidl_hal::OperandType& operandType) {
+    VERIFY_NON_NEGATIVE(underlyingType(operandType)) << "Negative operand types are not allowed.";
+    const auto canonical = static_cast<OperandType>(operandType);
+    if (canonical == OperandType::OEM || canonical == OperandType::TENSOR_OEM_BYTE) {
+        return NN_ERROR() << "Unable to convert invalid OperandType " << canonical;
+    }
+    return canonical;
+}
+
+GeneralResult<OperationType> unvalidatedConvert(const aidl_hal::OperationType& operationType) {
+    VERIFY_NON_NEGATIVE(underlyingType(operationType))
+            << "Negative operation types are not allowed.";
+    const auto canonical = static_cast<OperationType>(operationType);
+    if (canonical == OperationType::OEM_OPERATION) {
+        return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+    }
+    return canonical;
+}
+
+GeneralResult<DeviceType> unvalidatedConvert(const aidl_hal::DeviceType& deviceType) {
+    return static_cast<DeviceType>(deviceType);
+}
+
+GeneralResult<Priority> unvalidatedConvert(const aidl_hal::Priority& priority) {
+    return static_cast<Priority>(priority);
+}
+
+GeneralResult<Capabilities> unvalidatedConvert(const aidl_hal::Capabilities& capabilities) {
+    const bool validOperandTypes = std::all_of(
+            capabilities.operandPerformance.begin(), capabilities.operandPerformance.end(),
+            [](const aidl_hal::OperandPerformance& operandPerformance) {
+                return validatedConvert(operandPerformance.type).has_value();
+            });
+    if (!validOperandTypes) {
+        return NN_ERROR() << "Invalid OperandType when unvalidatedConverting OperandPerformance in "
+                             "Capabilities";
+    }
+
+    auto operandPerformance = NN_TRY(unvalidatedConvert(capabilities.operandPerformance));
+    auto table = NN_TRY(hal::utils::makeGeneralFailure(
+            Capabilities::OperandPerformanceTable::create(std::move(operandPerformance)),
+            nn::ErrorStatus::GENERAL_FAILURE));
+
+    return Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceScalar)),
+            .relaxedFloat32toFloat16PerformanceTensor = NN_TRY(
+                    unvalidatedConvert(capabilities.relaxedFloat32toFloat16PerformanceTensor)),
+            .operandPerformance = std::move(table),
+            .ifPerformance = NN_TRY(unvalidatedConvert(capabilities.ifPerformance)),
+            .whilePerformance = NN_TRY(unvalidatedConvert(capabilities.whilePerformance)),
+    };
+}
+
+GeneralResult<Capabilities::OperandPerformance> unvalidatedConvert(
+        const aidl_hal::OperandPerformance& operandPerformance) {
+    return Capabilities::OperandPerformance{
+            .type = NN_TRY(unvalidatedConvert(operandPerformance.type)),
+            .info = NN_TRY(unvalidatedConvert(operandPerformance.info)),
+    };
+}
+
+GeneralResult<Capabilities::PerformanceInfo> unvalidatedConvert(
+        const aidl_hal::PerformanceInfo& performanceInfo) {
+    return Capabilities::PerformanceInfo{
+            .execTime = performanceInfo.execTime,
+            .powerUsage = performanceInfo.powerUsage,
+    };
+}
+
+GeneralResult<DataLocation> unvalidatedConvert(const aidl_hal::DataLocation& location) {
+    VERIFY_NON_NEGATIVE(location.poolIndex) << "DataLocation: pool index must not be negative";
+    VERIFY_NON_NEGATIVE(location.offset) << "DataLocation: offset must not be negative";
+    VERIFY_NON_NEGATIVE(location.length) << "DataLocation: length must not be negative";
+    VERIFY_NON_NEGATIVE(location.padding) << "DataLocation: padding must not be negative";
+    if (location.offset > std::numeric_limits<uint32_t>::max()) {
+        return NN_ERROR() << "DataLocation: offset must be <= std::numeric_limits<uint32_t>::max()";
+    }
+    if (location.length > std::numeric_limits<uint32_t>::max()) {
+        return NN_ERROR() << "DataLocation: length must be <= std::numeric_limits<uint32_t>::max()";
+    }
+    if (location.padding > std::numeric_limits<uint32_t>::max()) {
+        return NN_ERROR()
+               << "DataLocation: padding must be <= std::numeric_limits<uint32_t>::max()";
+    }
+    return DataLocation{
+            .poolIndex = static_cast<uint32_t>(location.poolIndex),
+            .offset = static_cast<uint32_t>(location.offset),
+            .length = static_cast<uint32_t>(location.length),
+            .padding = static_cast<uint32_t>(location.padding),
+    };
+}
+
+GeneralResult<Operation> unvalidatedConvert(const aidl_hal::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = NN_TRY(toUnsigned(operation.inputs)),
+            .outputs = NN_TRY(toUnsigned(operation.outputs)),
+    };
+}
+
+GeneralResult<Operand::LifeTime> unvalidatedConvert(
+        const aidl_hal::OperandLifeTime& operandLifeTime) {
+    return static_cast<Operand::LifeTime>(operandLifeTime);
+}
+
+GeneralResult<Operand> unvalidatedConvert(const aidl_hal::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = NN_TRY(toUnsigned(operand.dimensions)),
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+GeneralResult<Operand::ExtraParams> unvalidatedConvert(
+        const std::optional<aidl_hal::OperandExtraParams>& optionalExtraParams) {
+    if (!optionalExtraParams.has_value()) {
+        return Operand::NoParams{};
+    }
+    const auto& extraParams = optionalExtraParams.value();
+    using Tag = aidl_hal::OperandExtraParams::Tag;
+    switch (extraParams.getTag()) {
+        case Tag::channelQuant:
+            return unvalidatedConvert(extraParams.get<Tag::channelQuant>());
+        case Tag::extension:
+            return extraParams.get<Tag::extension>();
+    }
+    return NN_ERROR() << "Unrecognized Operand::ExtraParams tag: "
+                      << underlyingType(extraParams.getTag());
+}
+
+GeneralResult<Operand::SymmPerChannelQuantParams> unvalidatedConvert(
+        const aidl_hal::SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+    VERIFY_NON_NEGATIVE(symmPerChannelQuantParams.channelDim)
+            << "Per-channel quantization channel dimension must not be negative.";
+    return Operand::SymmPerChannelQuantParams{
+            .scales = symmPerChannelQuantParams.scales,
+            .channelDim = static_cast<uint32_t>(symmPerChannelQuantParams.channelDim),
+    };
+}
+
+GeneralResult<Model> unvalidatedConvert(const aidl_hal::Model& model) {
+    return Model{
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+GeneralResult<Model::Subgraph> unvalidatedConvert(const aidl_hal::Subgraph& subgraph) {
+    return Model::Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
+            .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
+            .inputIndexes = NN_TRY(toUnsigned(subgraph.inputIndexes)),
+            .outputIndexes = NN_TRY(toUnsigned(subgraph.outputIndexes)),
+    };
+}
+
+GeneralResult<Model::ExtensionNameAndPrefix> unvalidatedConvert(
+        const aidl_hal::ExtensionNameAndPrefix& extensionNameAndPrefix) {
+    return Model::ExtensionNameAndPrefix{
+            .name = extensionNameAndPrefix.name,
+            .prefix = extensionNameAndPrefix.prefix,
+    };
+}
+
+GeneralResult<Extension> unvalidatedConvert(const aidl_hal::Extension& extension) {
+    return Extension{
+            .name = extension.name,
+            .operandTypes = NN_TRY(unvalidatedConvert(extension.operandTypes)),
+    };
+}
+
+GeneralResult<Extension::OperandTypeInformation> unvalidatedConvert(
+        const aidl_hal::ExtensionOperandTypeInformation& operandTypeInformation) {
+    VERIFY_NON_NEGATIVE(operandTypeInformation.byteSize)
+            << "Extension operand type byte size must not be negative";
+    return Extension::OperandTypeInformation{
+            .type = operandTypeInformation.type,
+            .isTensor = operandTypeInformation.isTensor,
+            .byteSize = static_cast<uint32_t>(operandTypeInformation.byteSize),
+    };
+}
+
+GeneralResult<OutputShape> unvalidatedConvert(const aidl_hal::OutputShape& outputShape) {
+    return OutputShape{
+            .dimensions = NN_TRY(toUnsigned(outputShape.dimensions)),
+            .isSufficient = outputShape.isSufficient,
+    };
+}
+
+GeneralResult<MeasureTiming> unvalidatedConvert(bool measureTiming) {
+    return measureTiming ? MeasureTiming::YES : MeasureTiming::NO;
+}
+
+GeneralResult<SharedMemory> unvalidatedConvert(const aidl_hal::Memory& memory) {
+    using Tag = aidl_hal::Memory::Tag;
+    switch (memory.getTag()) {
+        case Tag::ashmem: {
+            const auto& ashmem = memory.get<Tag::ashmem>();
+            VERIFY_NON_NEGATIVE(ashmem.size) << "Memory size must not be negative";
+            if (ashmem.size > std::numeric_limits<size_t>::max()) {
+                return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
+            }
+
+            auto handle = Memory::Ashmem{
+                    .fd = NN_TRY(dupFd(ashmem.fd.get())),
+                    .size = static_cast<size_t>(ashmem.size),
+            };
+            return std::make_shared<const Memory>(Memory{.handle = std::move(handle)});
+        }
+        case Tag::mappableFile: {
+            const auto& mappableFile = memory.get<Tag::mappableFile>();
+            VERIFY_NON_NEGATIVE(mappableFile.length) << "Memory size must not be negative";
+            VERIFY_NON_NEGATIVE(mappableFile.offset) << "Memory offset must not be negative";
+            if (mappableFile.length > std::numeric_limits<size_t>::max()) {
+                return NN_ERROR() << "Memory: size must be <= std::numeric_limits<size_t>::max()";
+            }
+            if (mappableFile.offset > std::numeric_limits<size_t>::max()) {
+                return NN_ERROR() << "Memory: offset must be <= std::numeric_limits<size_t>::max()";
+            }
+
+            const size_t size = static_cast<size_t>(mappableFile.length);
+            const int prot = mappableFile.prot;
+            const int fd = mappableFile.fd.get();
+            const size_t offset = static_cast<size_t>(mappableFile.offset);
+
+            return createSharedMemoryFromFd(size, prot, fd, offset);
+        }
+        case Tag::hardwareBuffer: {
+            const auto& hardwareBuffer = memory.get<Tag::hardwareBuffer>();
+
+            const UniqueNativeHandle handle =
+                    NN_TRY(nativeHandleFromAidlHandle(hardwareBuffer.handle));
+            const native_handle_t* nativeHandle = handle.get();
+
+            const AHardwareBuffer_Desc desc{
+                    .width = static_cast<uint32_t>(hardwareBuffer.description.width),
+                    .height = static_cast<uint32_t>(hardwareBuffer.description.height),
+                    .layers = static_cast<uint32_t>(hardwareBuffer.description.layers),
+                    .format = static_cast<uint32_t>(hardwareBuffer.description.format),
+                    .usage = static_cast<uint64_t>(hardwareBuffer.description.usage),
+                    .stride = static_cast<uint32_t>(hardwareBuffer.description.stride),
+            };
+            AHardwareBuffer* ahwb = nullptr;
+            const status_t status = AHardwareBuffer_createFromHandle(
+                    &desc, nativeHandle, AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE, &ahwb);
+            if (status != NO_ERROR) {
+                return NN_ERROR() << "createFromHandle failed";
+            }
+
+            return createSharedMemoryFromAHWB(ahwb, /*takeOwnership=*/true);
+        }
+    }
+    return NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag();
+}
+
+GeneralResult<Timing> unvalidatedConvert(const aidl_hal::Timing& timing) {
+    if (timing.timeInDriverNs < -1) {
+        return NN_ERROR() << "Timing: timeInDriverNs must not be less than -1";
+    }
+    if (timing.timeOnDeviceNs < -1) {
+        return NN_ERROR() << "Timing: timeOnDeviceNs must not be less than -1";
+    }
+    constexpr auto convertTiming = [](int64_t halTiming) -> OptionalDuration {
+        if (halTiming == kNoTiming) {
+            return {};
+        }
+        return nn::Duration(static_cast<uint64_t>(halTiming));
+    };
+    return Timing{.timeOnDevice = convertTiming(timing.timeOnDeviceNs),
+                  .timeInDriver = convertTiming(timing.timeInDriverNs)};
+}
+
+GeneralResult<Model::OperandValues> unvalidatedConvert(const std::vector<uint8_t>& operandValues) {
+    return Model::OperandValues(operandValues.data(), operandValues.size());
+}
+
+GeneralResult<BufferDesc> unvalidatedConvert(const aidl_hal::BufferDesc& bufferDesc) {
+    return BufferDesc{.dimensions = NN_TRY(toUnsigned(bufferDesc.dimensions))};
+}
+
+GeneralResult<BufferRole> unvalidatedConvert(const aidl_hal::BufferRole& bufferRole) {
+    VERIFY_NON_NEGATIVE(bufferRole.modelIndex) << "BufferRole: modelIndex must not be negative";
+    VERIFY_NON_NEGATIVE(bufferRole.ioIndex) << "BufferRole: ioIndex must not be negative";
+    return BufferRole{
+            .modelIndex = static_cast<uint32_t>(bufferRole.modelIndex),
+            .ioIndex = static_cast<uint32_t>(bufferRole.ioIndex),
+            .probability = bufferRole.probability,
+    };
+}
+
+GeneralResult<Request> unvalidatedConvert(const aidl_hal::Request& request) {
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
+    };
+}
+
+GeneralResult<Request::Argument> unvalidatedConvert(const aidl_hal::RequestArgument& argument) {
+    const auto lifetime = argument.hasNoValue ? Request::Argument::LifeTime::NO_VALUE
+                                              : Request::Argument::LifeTime::POOL;
+    return Request::Argument{
+            .lifetime = lifetime,
+            .location = NN_TRY(unvalidatedConvert(argument.location)),
+            .dimensions = NN_TRY(toUnsigned(argument.dimensions)),
+    };
+}
+
+GeneralResult<Request::MemoryPool> unvalidatedConvert(
+        const aidl_hal::RequestMemoryPool& memoryPool) {
+    using Tag = aidl_hal::RequestMemoryPool::Tag;
+    switch (memoryPool.getTag()) {
+        case Tag::pool:
+            return unvalidatedConvert(memoryPool.get<Tag::pool>());
+        case Tag::token: {
+            const auto token = memoryPool.get<Tag::token>();
+            VERIFY_NON_NEGATIVE(token) << "Memory pool token must not be negative";
+            return static_cast<Request::MemoryDomainToken>(token);
+        }
+    }
+    return NN_ERROR() << "Invalid Request::MemoryPool tag " << underlyingType(memoryPool.getTag());
+}
+
+GeneralResult<ErrorStatus> unvalidatedConvert(const aidl_hal::ErrorStatus& status) {
+    switch (status) {
+        case aidl_hal::ErrorStatus::NONE:
+        case aidl_hal::ErrorStatus::DEVICE_UNAVAILABLE:
+        case aidl_hal::ErrorStatus::GENERAL_FAILURE:
+        case aidl_hal::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case aidl_hal::ErrorStatus::INVALID_ARGUMENT:
+        case aidl_hal::ErrorStatus::MISSED_DEADLINE_TRANSIENT:
+        case aidl_hal::ErrorStatus::MISSED_DEADLINE_PERSISTENT:
+        case aidl_hal::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT:
+        case aidl_hal::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
+            return static_cast<ErrorStatus>(status);
+    }
+    return NN_ERROR() << "Invalid ErrorStatus " << underlyingType(status);
+}
+
+GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const aidl_hal::ExecutionPreference& executionPreference) {
+    return static_cast<ExecutionPreference>(executionPreference);
+}
+
+GeneralResult<std::vector<Operation>> unvalidatedConvert(
+        const std::vector<aidl_hal::Operation>& operations) {
+    return unvalidatedConvertVec(operations);
+}
+
+GeneralResult<SharedHandle> unvalidatedConvert(const ndk::ScopedFileDescriptor& handle) {
+    auto duplicatedFd = NN_TRY(dupFd(handle.get()));
+    return std::make_shared<const Handle>(std::move(duplicatedFd));
+}
+
+GeneralResult<Capabilities> convert(const aidl_hal::Capabilities& capabilities) {
+    return validatedConvert(capabilities);
+}
+
+GeneralResult<DeviceType> convert(const aidl_hal::DeviceType& deviceType) {
+    return validatedConvert(deviceType);
+}
+
+GeneralResult<ErrorStatus> convert(const aidl_hal::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+GeneralResult<ExecutionPreference> convert(
+        const aidl_hal::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
+GeneralResult<SharedMemory> convert(const aidl_hal::Memory& operand) {
+    return validatedConvert(operand);
+}
+
+GeneralResult<Model> convert(const aidl_hal::Model& model) {
+    return validatedConvert(model);
+}
+
+GeneralResult<OperandType> convert(const aidl_hal::OperandType& operandType) {
+    return validatedConvert(operandType);
+}
+
+GeneralResult<Priority> convert(const aidl_hal::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+GeneralResult<Request> convert(const aidl_hal::Request& request) {
+    return validatedConvert(request);
+}
+
+GeneralResult<Timing> convert(const aidl_hal::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+GeneralResult<SharedHandle> convert(const ndk::ScopedFileDescriptor& handle) {
+    return validatedConvert(handle);
+}
+
+GeneralResult<std::vector<Extension>> convert(const std::vector<aidl_hal::Extension>& extension) {
+    return validatedConvert(extension);
+}
+
+GeneralResult<std::vector<SharedMemory>> convert(const std::vector<aidl_hal::Memory>& memories) {
+    return validatedConvert(memories);
+}
+
+GeneralResult<std::vector<OutputShape>> convert(
+        const std::vector<aidl_hal::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
+}
+
+GeneralResult<std::vector<uint32_t>> toUnsigned(const std::vector<int32_t>& vec) {
+    if (!std::all_of(vec.begin(), vec.end(), [](int32_t v) { return v >= 0; })) {
+        return NN_ERROR() << "Negative value passed to conversion from signed to unsigned";
+    }
+    return std::vector<uint32_t>(vec.begin(), vec.end());
+}
+
+}  // namespace android::nn
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename Input>
+using UnvalidatedConvertOutput =
+        std::decay_t<decltype(unvalidatedConvert(std::declval<Input>()).value())>;
+
+template <typename Type>
+nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvertVec(
+        const std::vector<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> halObject;
+    halObject.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        halObject.push_back(NN_TRY(unvalidatedConvert(argument)));
+    }
+    return halObject;
+}
+
+template <typename Type>
+nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> unvalidatedConvert(
+        const std::vector<Type>& arguments) {
+    return unvalidatedConvertVec(arguments);
+}
+
+template <typename Type>
+nn::GeneralResult<UnvalidatedConvertOutput<Type>> validatedConvert(const Type& canonical) {
+    NN_TRY(compliantVersion(canonical));
+    return utils::unvalidatedConvert(canonical);
+}
+
+template <typename Type>
+nn::GeneralResult<std::vector<UnvalidatedConvertOutput<Type>>> validatedConvert(
+        const std::vector<Type>& arguments) {
+    std::vector<UnvalidatedConvertOutput<Type>> halObject(arguments.size());
+    for (size_t i = 0; i < arguments.size(); ++i) {
+        halObject[i] = NN_TRY(validatedConvert(arguments[i]));
+    }
+    return halObject;
+}
+
+// Helper template for std::visit
+template <class... Ts>
+struct overloaded : Ts... {
+    using Ts::operator()...;
+};
+template <class... Ts>
+overloaded(Ts...)->overloaded<Ts...>;
+
+nn::GeneralResult<common::NativeHandle> aidlHandleFromNativeHandle(
+        const native_handle_t& nativeHandle) {
+    auto handle = ::android::dupToAidl(&nativeHandle);
+    if (!std::all_of(handle.fds.begin(), handle.fds.end(),
+                     [](const ndk::ScopedFileDescriptor& fd) { return fd.get() >= 0; })) {
+        return NN_ERROR() << "android::dupToAidl returned an invalid common::NativeHandle";
+    }
+    return handle;
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Ashmem& memory) {
+    if constexpr (std::numeric_limits<size_t>::max() > std::numeric_limits<int64_t>::max()) {
+        if (memory.size > std::numeric_limits<int64_t>::max()) {
+            return (
+                           NN_ERROR()
+                           << "Memory::Ashmem: size must be <= std::numeric_limits<int64_t>::max()")
+                    .
+                    operator nn::GeneralResult<Memory>();
+        }
+    }
+
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+    auto handle = common::Ashmem{
+            .fd = ndk::ScopedFileDescriptor(fd.release()),
+            .size = static_cast<int64_t>(memory.size),
+    };
+    return Memory::make<Memory::Tag::ashmem>(std::move(handle));
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Fd& memory) {
+    if constexpr (std::numeric_limits<size_t>::max() > std::numeric_limits<int64_t>::max()) {
+        if (memory.size > std::numeric_limits<int64_t>::max()) {
+            return (NN_ERROR() << "Memory::Fd: size must be <= std::numeric_limits<int64_t>::max()")
+                    .
+                    operator nn::GeneralResult<Memory>();
+        }
+        if (memory.offset > std::numeric_limits<int64_t>::max()) {
+            return (
+                           NN_ERROR()
+                           << "Memory::Fd: offset must be <= std::numeric_limits<int64_t>::max()")
+                    .
+                    operator nn::GeneralResult<Memory>();
+        }
+    }
+
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+    auto handle = common::MappableFile{
+            .length = static_cast<int64_t>(memory.size),
+            .prot = memory.prot,
+            .fd = ndk::ScopedFileDescriptor(fd.release()),
+            .offset = static_cast<int64_t>(memory.offset),
+    };
+    return Memory::make<Memory::Tag::mappableFile>(std::move(handle));
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::HardwareBuffer& memory) {
+    const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(memory.handle.get());
+    if (nativeHandle == nullptr) {
+        return (NN_ERROR() << "unvalidatedConvert failed because AHardwareBuffer_getNativeHandle "
+                              "returned nullptr")
+                .
+                operator nn::GeneralResult<Memory>();
+    }
+
+    auto handle = NN_TRY(aidlHandleFromNativeHandle(*nativeHandle));
+
+    AHardwareBuffer_Desc desc;
+    AHardwareBuffer_describe(memory.handle.get(), &desc);
+
+    const auto description = graphics::common::HardwareBufferDescription{
+            .width = static_cast<int32_t>(desc.width),
+            .height = static_cast<int32_t>(desc.height),
+            .layers = static_cast<int32_t>(desc.layers),
+            .format = static_cast<graphics::common::PixelFormat>(desc.format),
+            .usage = static_cast<graphics::common::BufferUsage>(desc.usage),
+            .stride = static_cast<int32_t>(desc.stride),
+    };
+
+    auto hardwareBuffer = graphics::common::HardwareBuffer{
+            .description = std::move(description),
+            .handle = std::move(handle),
+    };
+    return Memory::make<Memory::Tag::hardwareBuffer>(std::move(hardwareBuffer));
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::Memory::Unknown& /*memory*/) {
+    return (NN_ERROR() << "Unable to convert Unknown memory type")
+            .
+            operator nn::GeneralResult<Memory>();
+}
+
+}  // namespace
+
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(const nn::CacheToken& cacheToken) {
+    return std::vector<uint8_t>(cacheToken.begin(), cacheToken.end());
+}
+
+nn::GeneralResult<BufferDesc> unvalidatedConvert(const nn::BufferDesc& bufferDesc) {
+    return BufferDesc{.dimensions = NN_TRY(toSigned(bufferDesc.dimensions))};
+}
+
+nn::GeneralResult<BufferRole> unvalidatedConvert(const nn::BufferRole& bufferRole) {
+    VERIFY_LE_INT32_MAX(bufferRole.modelIndex)
+            << "BufferRole: modelIndex must be <= std::numeric_limits<int32_t>::max()";
+    VERIFY_LE_INT32_MAX(bufferRole.ioIndex)
+            << "BufferRole: ioIndex must be <= std::numeric_limits<int32_t>::max()";
+    return BufferRole{
+            .modelIndex = static_cast<int32_t>(bufferRole.modelIndex),
+            .ioIndex = static_cast<int32_t>(bufferRole.ioIndex),
+            .probability = bufferRole.probability,
+    };
+}
+
+nn::GeneralResult<bool> unvalidatedConvert(const nn::MeasureTiming& measureTiming) {
+    return measureTiming == nn::MeasureTiming::YES;
+}
+
+nn::GeneralResult<Memory> unvalidatedConvert(const nn::SharedMemory& memory) {
+    if (memory == nullptr) {
+        return (NN_ERROR() << "Unable to convert nullptr memory")
+                .
+                operator nn::GeneralResult<Memory>();
+    }
+    return std::visit([](const auto& x) { return unvalidatedConvert(x); }, memory->handle);
+}
+
+nn::GeneralResult<ErrorStatus> unvalidatedConvert(const nn::ErrorStatus& errorStatus) {
+    switch (errorStatus) {
+        case nn::ErrorStatus::NONE:
+        case nn::ErrorStatus::DEVICE_UNAVAILABLE:
+        case nn::ErrorStatus::GENERAL_FAILURE:
+        case nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE:
+        case nn::ErrorStatus::INVALID_ARGUMENT:
+        case nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT:
+        case nn::ErrorStatus::MISSED_DEADLINE_PERSISTENT:
+        case nn::ErrorStatus::RESOURCE_EXHAUSTED_TRANSIENT:
+        case nn::ErrorStatus::RESOURCE_EXHAUSTED_PERSISTENT:
+            return static_cast<ErrorStatus>(errorStatus);
+        default:
+            return ErrorStatus::GENERAL_FAILURE;
+    }
+}
+
+nn::GeneralResult<OutputShape> unvalidatedConvert(const nn::OutputShape& outputShape) {
+    return OutputShape{.dimensions = NN_TRY(toSigned(outputShape.dimensions)),
+                       .isSufficient = outputShape.isSufficient};
+}
+
+nn::GeneralResult<ExecutionPreference> unvalidatedConvert(
+        const nn::ExecutionPreference& executionPreference) {
+    return static_cast<ExecutionPreference>(executionPreference);
+}
+
+nn::GeneralResult<OperandType> unvalidatedConvert(const nn::OperandType& operandType) {
+    if (operandType == nn::OperandType::OEM || operandType == nn::OperandType::TENSOR_OEM_BYTE) {
+        return NN_ERROR() << "Unable to convert invalid OperandType " << operandType;
+    }
+    return static_cast<OperandType>(operandType);
+}
+
+nn::GeneralResult<OperandLifeTime> unvalidatedConvert(
+        const nn::Operand::LifeTime& operandLifeTime) {
+    return static_cast<OperandLifeTime>(operandLifeTime);
+}
+
+nn::GeneralResult<DataLocation> unvalidatedConvert(const nn::DataLocation& location) {
+    VERIFY_LE_INT32_MAX(location.poolIndex)
+            << "DataLocation: pool index must be <= std::numeric_limits<int32_t>::max()";
+    return DataLocation{
+            .poolIndex = static_cast<int32_t>(location.poolIndex),
+            .offset = static_cast<int64_t>(location.offset),
+            .length = static_cast<int64_t>(location.length),
+    };
+}
+
+nn::GeneralResult<std::optional<OperandExtraParams>> unvalidatedConvert(
+        const nn::Operand::ExtraParams& extraParams) {
+    return std::visit(
+            overloaded{
+                    [](const nn::Operand::NoParams&)
+                            -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+                        return std::nullopt;
+                    },
+                    [](const nn::Operand::SymmPerChannelQuantParams& symmPerChannelQuantParams)
+                            -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+                        if (symmPerChannelQuantParams.channelDim >
+                            std::numeric_limits<int32_t>::max()) {
+                            // Using explicit type conversion because std::optional in successful
+                            // result confuses the compiler.
+                            return (NN_ERROR() << "symmPerChannelQuantParams.channelDim must be <= "
+                                                  "std::numeric_limits<int32_t>::max(), received: "
+                                               << symmPerChannelQuantParams.channelDim)
+                                    .
+                                    operator nn::GeneralResult<std::optional<OperandExtraParams>>();
+                        }
+                        return OperandExtraParams::make<OperandExtraParams::Tag::channelQuant>(
+                                SymmPerChannelQuantParams{
+                                        .scales = symmPerChannelQuantParams.scales,
+                                        .channelDim = static_cast<int32_t>(
+                                                symmPerChannelQuantParams.channelDim),
+                                });
+                    },
+                    [](const nn::Operand::ExtensionParams& extensionParams)
+                            -> nn::GeneralResult<std::optional<OperandExtraParams>> {
+                        return OperandExtraParams::make<OperandExtraParams::Tag::extension>(
+                                extensionParams);
+                    },
+            },
+            extraParams);
+}
+
+nn::GeneralResult<Operand> unvalidatedConvert(const nn::Operand& operand) {
+    return Operand{
+            .type = NN_TRY(unvalidatedConvert(operand.type)),
+            .dimensions = NN_TRY(toSigned(operand.dimensions)),
+            .scale = operand.scale,
+            .zeroPoint = operand.zeroPoint,
+            .lifetime = NN_TRY(unvalidatedConvert(operand.lifetime)),
+            .location = NN_TRY(unvalidatedConvert(operand.location)),
+            .extraParams = NN_TRY(unvalidatedConvert(operand.extraParams)),
+    };
+}
+
+nn::GeneralResult<OperationType> unvalidatedConvert(const nn::OperationType& operationType) {
+    if (operationType == nn::OperationType::OEM_OPERATION) {
+        return NN_ERROR() << "Unable to convert invalid OperationType OEM_OPERATION";
+    }
+    return static_cast<OperationType>(operationType);
+}
+
+nn::GeneralResult<Operation> unvalidatedConvert(const nn::Operation& operation) {
+    return Operation{
+            .type = NN_TRY(unvalidatedConvert(operation.type)),
+            .inputs = NN_TRY(toSigned(operation.inputs)),
+            .outputs = NN_TRY(toSigned(operation.outputs)),
+    };
+}
+
+nn::GeneralResult<Subgraph> unvalidatedConvert(const nn::Model::Subgraph& subgraph) {
+    return Subgraph{
+            .operands = NN_TRY(unvalidatedConvert(subgraph.operands)),
+            .operations = NN_TRY(unvalidatedConvert(subgraph.operations)),
+            .inputIndexes = NN_TRY(toSigned(subgraph.inputIndexes)),
+            .outputIndexes = NN_TRY(toSigned(subgraph.outputIndexes)),
+    };
+}
+
+nn::GeneralResult<std::vector<uint8_t>> unvalidatedConvert(
+        const nn::Model::OperandValues& operandValues) {
+    return std::vector<uint8_t>(operandValues.data(), operandValues.data() + operandValues.size());
+}
+
+nn::GeneralResult<ExtensionNameAndPrefix> unvalidatedConvert(
+        const nn::Model::ExtensionNameAndPrefix& extensionNameToPrefix) {
+    return ExtensionNameAndPrefix{
+            .name = extensionNameToPrefix.name,
+            .prefix = extensionNameToPrefix.prefix,
+    };
+}
+
+nn::GeneralResult<Model> unvalidatedConvert(const nn::Model& model) {
+    return Model{
+            .main = NN_TRY(unvalidatedConvert(model.main)),
+            .referenced = NN_TRY(unvalidatedConvert(model.referenced)),
+            .operandValues = NN_TRY(unvalidatedConvert(model.operandValues)),
+            .pools = NN_TRY(unvalidatedConvert(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = NN_TRY(unvalidatedConvert(model.extensionNameToPrefix)),
+    };
+}
+
+nn::GeneralResult<Priority> unvalidatedConvert(const nn::Priority& priority) {
+    return static_cast<Priority>(priority);
+}
+
+nn::GeneralResult<Request> unvalidatedConvert(const nn::Request& request) {
+    return Request{
+            .inputs = NN_TRY(unvalidatedConvert(request.inputs)),
+            .outputs = NN_TRY(unvalidatedConvert(request.outputs)),
+            .pools = NN_TRY(unvalidatedConvert(request.pools)),
+    };
+}
+
+nn::GeneralResult<RequestArgument> unvalidatedConvert(
+        const nn::Request::Argument& requestArgument) {
+    if (requestArgument.lifetime == nn::Request::Argument::LifeTime::POINTER) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "Request cannot be unvalidatedConverted because it contains pointer-based memory";
+    }
+    const bool hasNoValue = requestArgument.lifetime == nn::Request::Argument::LifeTime::NO_VALUE;
+    return RequestArgument{
+            .hasNoValue = hasNoValue,
+            .location = NN_TRY(unvalidatedConvert(requestArgument.location)),
+            .dimensions = NN_TRY(toSigned(requestArgument.dimensions)),
+    };
+}
+
+nn::GeneralResult<RequestMemoryPool> unvalidatedConvert(const nn::Request::MemoryPool& memoryPool) {
+    return std::visit(
+            overloaded{
+                    [](const nn::SharedMemory& memory) -> nn::GeneralResult<RequestMemoryPool> {
+                        return RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+                                NN_TRY(unvalidatedConvert(memory)));
+                    },
+                    [](const nn::Request::MemoryDomainToken& token)
+                            -> nn::GeneralResult<RequestMemoryPool> {
+                        return RequestMemoryPool::make<RequestMemoryPool::Tag::token>(
+                                underlyingType(token));
+                    },
+                    [](const nn::SharedBuffer& /*buffer*/) {
+                        return (NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+                                << "Unable to make memory pool from IBuffer")
+                                .
+                                operator nn::GeneralResult<RequestMemoryPool>();
+                    },
+            },
+            memoryPool);
+}
+
+nn::GeneralResult<Timing> unvalidatedConvert(const nn::Timing& timing) {
+    return Timing{
+            .timeOnDeviceNs = NN_TRY(unvalidatedConvert(timing.timeOnDevice)),
+            .timeInDriverNs = NN_TRY(unvalidatedConvert(timing.timeInDriver)),
+    };
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::Duration& duration) {
+    if (duration < nn::Duration::zero()) {
+        return NN_ERROR() << "Unable to convert invalid (negative) duration";
+    }
+    constexpr std::chrono::nanoseconds::rep kIntMax = std::numeric_limits<int64_t>::max();
+    const auto count = duration.count();
+    return static_cast<int64_t>(std::min(count, kIntMax));
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalDuration& optionalDuration) {
+    if (!optionalDuration.has_value()) {
+        return kNoTiming;
+    }
+    return unvalidatedConvert(optionalDuration.value());
+}
+
+nn::GeneralResult<int64_t> unvalidatedConvert(const nn::OptionalTimePoint& optionalTimePoint) {
+    if (!optionalTimePoint.has_value()) {
+        return kNoTiming;
+    }
+    return unvalidatedConvert(optionalTimePoint->time_since_epoch());
+}
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SyncFence& syncFence) {
+    auto duplicatedFd = NN_TRY(nn::dupFd(syncFence.getFd()));
+    return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> unvalidatedConvert(const nn::SharedHandle& handle) {
+    auto duplicatedFd = NN_TRY(nn::dupFd(handle->get()));
+    return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<std::vector<uint8_t>> convert(const nn::CacheToken& cacheToken) {
+    return validatedConvert(cacheToken);
+}
+
+nn::GeneralResult<BufferDesc> convert(const nn::BufferDesc& bufferDesc) {
+    return validatedConvert(bufferDesc);
+}
+
+nn::GeneralResult<bool> convert(const nn::MeasureTiming& measureTiming) {
+    return validatedConvert(measureTiming);
+}
+
+nn::GeneralResult<Memory> convert(const nn::SharedMemory& memory) {
+    return validatedConvert(memory);
+}
+
+nn::GeneralResult<ErrorStatus> convert(const nn::ErrorStatus& errorStatus) {
+    return validatedConvert(errorStatus);
+}
+
+nn::GeneralResult<ExecutionPreference> convert(const nn::ExecutionPreference& executionPreference) {
+    return validatedConvert(executionPreference);
+}
+
+nn::GeneralResult<Model> convert(const nn::Model& model) {
+    return validatedConvert(model);
+}
+
+nn::GeneralResult<Priority> convert(const nn::Priority& priority) {
+    return validatedConvert(priority);
+}
+
+nn::GeneralResult<Request> convert(const nn::Request& request) {
+    return validatedConvert(request);
+}
+
+nn::GeneralResult<Timing> convert(const nn::Timing& timing) {
+    return validatedConvert(timing);
+}
+
+nn::GeneralResult<int64_t> convert(const nn::OptionalDuration& optionalDuration) {
+    return validatedConvert(optionalDuration);
+}
+
+nn::GeneralResult<int64_t> convert(const nn::OptionalTimePoint& outputShapes) {
+    return validatedConvert(outputShapes);
+}
+
+nn::GeneralResult<std::vector<BufferRole>> convert(const std::vector<nn::BufferRole>& bufferRoles) {
+    return validatedConvert(bufferRoles);
+}
+
+nn::GeneralResult<std::vector<OutputShape>> convert(
+        const std::vector<nn::OutputShape>& outputShapes) {
+    return validatedConvert(outputShapes);
+}
+
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+        const std::vector<nn::SharedHandle>& cacheHandles) {
+    return validatedConvert(cacheHandles);
+}
+
+nn::GeneralResult<std::vector<ndk::ScopedFileDescriptor>> convert(
+        const std::vector<nn::SyncFence>& syncFences) {
+    return validatedConvert(syncFences);
+}
+
+nn::GeneralResult<std::vector<int32_t>> toSigned(const std::vector<uint32_t>& vec) {
+    if (!std::all_of(vec.begin(), vec.end(),
+                     [](uint32_t v) { return v <= std::numeric_limits<int32_t>::max(); })) {
+        return NN_ERROR() << "Vector contains a value that doesn't fit into int32_t.";
+    }
+    return std::vector<int32_t>(vec.begin(), vec.end());
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Device.cpp b/neuralnetworks/aidl/utils/src/Device.cpp
new file mode 100644
index 0000000..e80de0b
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Device.cpp
@@ -0,0 +1,290 @@
+/*
+ * 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.
+ */
+
+#include "Device.h"
+
+#include "Buffer.h"
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/OperandTypes.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+
+#include <any>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <string>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+namespace {
+
+nn::GeneralResult<std::vector<std::shared_ptr<IPreparedModel>>> convert(
+        const std::vector<nn::SharedPreparedModel>& preparedModels) {
+    std::vector<std::shared_ptr<IPreparedModel>> aidlPreparedModels(preparedModels.size());
+    for (size_t i = 0; i < preparedModels.size(); ++i) {
+        std::any underlyingResource = preparedModels[i]->getUnderlyingResource();
+        if (const auto* aidlPreparedModel =
+                    std::any_cast<std::shared_ptr<aidl_hal::IPreparedModel>>(&underlyingResource)) {
+            aidlPreparedModels[i] = *aidlPreparedModel;
+        } else {
+            return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+                   << "Unable to convert from nn::IPreparedModel to aidl_hal::IPreparedModel";
+        }
+    }
+    return aidlPreparedModels;
+}
+
+nn::GeneralResult<nn::Capabilities> getCapabilitiesFrom(IDevice* device) {
+    CHECK(device != nullptr);
+    Capabilities capabilities;
+    const auto ret = device->getCapabilities(&capabilities);
+    HANDLE_ASTATUS(ret) << "getCapabilities failed";
+    return nn::convert(capabilities);
+}
+
+nn::GeneralResult<std::string> getVersionStringFrom(aidl_hal::IDevice* device) {
+    CHECK(device != nullptr);
+    std::string version;
+    const auto ret = device->getVersionString(&version);
+    HANDLE_ASTATUS(ret) << "getVersionString failed";
+    return version;
+}
+
+nn::GeneralResult<nn::DeviceType> getDeviceTypeFrom(aidl_hal::IDevice* device) {
+    CHECK(device != nullptr);
+    DeviceType deviceType;
+    const auto ret = device->getType(&deviceType);
+    HANDLE_ASTATUS(ret) << "getDeviceType failed";
+    return nn::convert(deviceType);
+}
+
+nn::GeneralResult<std::vector<nn::Extension>> getSupportedExtensionsFrom(
+        aidl_hal::IDevice* device) {
+    CHECK(device != nullptr);
+    std::vector<Extension> supportedExtensions;
+    const auto ret = device->getSupportedExtensions(&supportedExtensions);
+    HANDLE_ASTATUS(ret) << "getExtensions failed";
+    return nn::convert(supportedExtensions);
+}
+
+nn::GeneralResult<std::pair<uint32_t, uint32_t>> getNumberOfCacheFilesNeededFrom(
+        aidl_hal::IDevice* device) {
+    CHECK(device != nullptr);
+    NumberOfCacheFiles numberOfCacheFiles;
+    const auto ret = device->getNumberOfCacheFilesNeeded(&numberOfCacheFiles);
+    HANDLE_ASTATUS(ret) << "getNumberOfCacheFilesNeeded failed";
+
+    if (numberOfCacheFiles.numDataCache < 0 || numberOfCacheFiles.numModelCache < 0) {
+        return NN_ERROR() << "Driver reported negative numer of cache files needed";
+    }
+    if (static_cast<uint32_t>(numberOfCacheFiles.numModelCache) > nn::kMaxNumberOfCacheFiles) {
+        return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numModelCache files greater "
+                             "than allowed max ("
+                          << numberOfCacheFiles.numModelCache << " vs "
+                          << nn::kMaxNumberOfCacheFiles << ")";
+    }
+    if (static_cast<uint32_t>(numberOfCacheFiles.numDataCache) > nn::kMaxNumberOfCacheFiles) {
+        return NN_ERROR() << "getNumberOfCacheFilesNeeded returned numDataCache files greater "
+                             "than allowed max ("
+                          << numberOfCacheFiles.numDataCache << " vs " << nn::kMaxNumberOfCacheFiles
+                          << ")";
+    }
+    return std::make_pair(numberOfCacheFiles.numModelCache, numberOfCacheFiles.numDataCache);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const Device>> Device::create(
+        std::string name, std::shared_ptr<aidl_hal::IDevice> device) {
+    if (name.empty()) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "aidl_hal::utils::Device::create must have non-empty name";
+    }
+    if (device == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "aidl_hal::utils::Device::create must have non-null device";
+    }
+
+    auto versionString = NN_TRY(getVersionStringFrom(device.get()));
+    const auto deviceType = NN_TRY(getDeviceTypeFrom(device.get()));
+    auto extensions = NN_TRY(getSupportedExtensionsFrom(device.get()));
+    auto capabilities = NN_TRY(getCapabilitiesFrom(device.get()));
+    const auto numberOfCacheFilesNeeded = NN_TRY(getNumberOfCacheFilesNeededFrom(device.get()));
+
+    auto deathHandler = NN_TRY(DeathHandler::create(device));
+    return std::make_shared<const Device>(
+            PrivateConstructorTag{}, std::move(name), std::move(versionString), deviceType,
+            std::move(extensions), std::move(capabilities), numberOfCacheFilesNeeded,
+            std::move(device), std::move(deathHandler));
+}
+
+Device::Device(PrivateConstructorTag /*tag*/, std::string name, std::string versionString,
+               nn::DeviceType deviceType, std::vector<nn::Extension> extensions,
+               nn::Capabilities capabilities,
+               std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded,
+               std::shared_ptr<aidl_hal::IDevice> device, DeathHandler deathHandler)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kDeviceType(deviceType),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded),
+      kDevice(std::move(device)),
+      kDeathHandler(std::move(deathHandler)) {}
+
+const std::string& Device::getName() const {
+    return kName;
+}
+
+const std::string& Device::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version Device::getFeatureLevel() const {
+    return nn::Version::ANDROID_S;
+}
+
+nn::DeviceType Device::getType() const {
+    return kDeviceType;
+}
+
+const std::vector<nn::Extension>& Device::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& Device::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> Device::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> Device::wait() const {
+    const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_ping(kDevice->asBinder().get()));
+    HANDLE_ASTATUS(ret) << "ping failed";
+    return {};
+}
+
+nn::GeneralResult<std::vector<bool>> Device::getSupportedOperations(const nn::Model& model) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto aidlModel = NN_TRY(convert(modelInShared));
+
+    std::vector<bool> supportedOperations;
+    const auto ret = kDevice->getSupportedOperations(aidlModel, &supportedOperations);
+    HANDLE_ASTATUS(ret) << "getSupportedOperations failed";
+
+    return supportedOperations;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModel(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    // Ensure that model is ready for IPC.
+    std::optional<nn::Model> maybeModelInShared;
+    const nn::Model& modelInShared =
+            NN_TRY(hal::utils::flushDataFromPointerToShared(&model, &maybeModelInShared));
+
+    const auto aidlModel = NN_TRY(convert(modelInShared));
+    const auto aidlPreference = NN_TRY(convert(preference));
+    const auto aidlPriority = NN_TRY(convert(priority));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlModelCache = NN_TRY(convert(modelCache));
+    const auto aidlDataCache = NN_TRY(convert(dataCache));
+    const auto aidlToken = NN_TRY(convert(token));
+
+    const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModel(aidlModel, aidlPreference, aidlPriority, aidlDeadline,
+                                           aidlModelCache, aidlDataCache, aidlToken, cb);
+    HANDLE_ASTATUS(ret) << "prepareModel failed";
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> Device::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlModelCache = NN_TRY(convert(modelCache));
+    const auto aidlDataCache = NN_TRY(convert(dataCache));
+    const auto aidlToken = NN_TRY(convert(token));
+
+    const auto cb = ndk::SharedRefBase::make<PreparedModelCallback>();
+    const auto scoped = kDeathHandler.protectCallback(cb.get());
+
+    const auto ret = kDevice->prepareModelFromCache(aidlDeadline, aidlModelCache, aidlDataCache,
+                                                    aidlToken, cb);
+    HANDLE_ASTATUS(ret) << "prepareModelFromCache failed";
+
+    return cb->get();
+}
+
+nn::GeneralResult<nn::SharedBuffer> Device::allocate(
+        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 aidlDesc = NN_TRY(convert(desc));
+    const auto aidlPreparedModels = NN_TRY(convert(preparedModels));
+    const auto aidlInputRoles = NN_TRY(convert(inputRoles));
+    const auto aidlOutputRoles = NN_TRY(convert(outputRoles));
+
+    std::vector<IPreparedModelParcel> aidlPreparedModelParcels;
+    aidlPreparedModelParcels.reserve(aidlPreparedModels.size());
+    for (const auto& preparedModel : aidlPreparedModels) {
+        aidlPreparedModelParcels.push_back({.preparedModel = preparedModel});
+    }
+
+    DeviceBuffer buffer;
+    const auto ret = kDevice->allocate(aidlDesc, aidlPreparedModelParcels, aidlInputRoles,
+                                       aidlOutputRoles, &buffer);
+    HANDLE_ASTATUS(ret) << "IDevice::allocate failed";
+
+    if (buffer.token < 0) {
+        return NN_ERROR() << "IDevice::allocate returned negative token";
+    }
+
+    return Buffer::create(buffer.buffer, static_cast<nn::Request::MemoryDomainToken>(buffer.token));
+}
+
+DeathMonitor* Device::getDeathMonitor() const {
+    return kDeathHandler.getDeathMonitor().get();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Execution.cpp b/neuralnetworks/aidl/utils/src/Execution.cpp
new file mode 100644
index 0000000..2aee8a6
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Execution.cpp
@@ -0,0 +1,79 @@
+/*
+ * 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.
+ */
+
+#include "Execution.h"
+
+#include "Conversions.h"
+#include "PreparedModel.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on HIDL interface
+// lifetimes across processes and for protecting asynchronous calls across HIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<std::shared_ptr<const Execution>> Execution::create(
+        std::shared_ptr<const PreparedModel> preparedModel, Request request,
+        hal::utils::RequestRelocation relocation, bool measure, int64_t loopTimeoutDuration) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR() << "aidl::utils::Execution::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const Execution>(PrivateConstructorTag{}, std::move(preparedModel),
+                                             std::move(request), std::move(relocation), measure,
+                                             loopTimeoutDuration);
+}
+
+Execution::Execution(PrivateConstructorTag /*tag*/,
+                     std::shared_ptr<const PreparedModel> preparedModel, Request request,
+                     hal::utils::RequestRelocation relocation, bool measure,
+                     int64_t loopTimeoutDuration)
+    : kPreparedModel(std::move(preparedModel)),
+      kRequest(std::move(request)),
+      kRelocation(std::move(relocation)),
+      kMeasure(measure),
+      kLoopTimeoutDuration(loopTimeoutDuration) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> Execution::compute(
+        const nn::OptionalTimePoint& deadline) const {
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    return kPreparedModel->executeInternal(kRequest, kMeasure, aidlDeadline, kLoopTimeoutDuration,
+                                           kRelocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> Execution::computeFenced(
+        const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto aidlWaitFor = NN_TRY(convert(waitFor));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return kPreparedModel->executeFencedInternal(kRequest, aidlWaitFor, kMeasure, aidlDeadline,
+                                                 kLoopTimeoutDuration,
+                                                 aidlTimeoutDurationAfterFence, kRelocation);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/InvalidDevice.cpp b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp
new file mode 100644
index 0000000..c9d9955
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/InvalidDevice.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "InvalidDevice"
+
+#include "InvalidDevice.h"
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_auto_utils.h>
+
+#include "Conversions.h"
+#include "Utils.h"
+
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+namespace aidl::android::hardware::neuralnetworks {
+namespace {
+
+ndk::ScopedAStatus toAStatus(ErrorStatus errorStatus, const std::string& errorMessage) {
+    if (errorStatus == ErrorStatus::NONE) {
+        return ndk::ScopedAStatus::ok();
+    }
+    return ndk::ScopedAStatus::fromServiceSpecificErrorWithMessage(
+            static_cast<int32_t>(errorStatus), errorMessage.c_str());
+}
+
+}  // namespace
+
+std::shared_ptr<InvalidDevice> InvalidDevice::create() {
+    constexpr auto perf = PerformanceInfo{
+            .execTime = std::numeric_limits<float>::max(),
+            .powerUsage = std::numeric_limits<float>::max(),
+    };
+    auto capabilities = Capabilities{
+            .relaxedFloat32toFloat16PerformanceScalar = perf,
+            .relaxedFloat32toFloat16PerformanceTensor = perf,
+            .operandPerformance = {},
+            .ifPerformance = perf,
+            .whilePerformance = perf,
+    };
+    constexpr auto numberOfCacheFiles = NumberOfCacheFiles{
+            .numModelCache = 0,
+            .numDataCache = 0,
+    };
+    std::vector<Extension> extensions{};
+    constexpr auto deviceType = DeviceType::OTHER;
+    std::string versionString = "invalid";
+
+    return ndk::SharedRefBase::make<InvalidDevice>(std::move(capabilities), numberOfCacheFiles,
+                                                   std::move(extensions), deviceType,
+                                                   std::move(versionString));
+}
+
+InvalidDevice::InvalidDevice(Capabilities capabilities,
+                             const NumberOfCacheFiles& numberOfCacheFiles,
+                             std::vector<Extension> extensions, DeviceType deviceType,
+                             std::string versionString)
+    : kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFiles(numberOfCacheFiles),
+      kExtensions(std::move(extensions)),
+      kDeviceType(deviceType),
+      kVersionString(std::move(versionString)) {}
+
+ndk::ScopedAStatus InvalidDevice::allocate(
+        const BufferDesc& /*desc*/, const std::vector<IPreparedModelParcel>& /*preparedModels*/,
+        const std::vector<BufferRole>& /*inputRoles*/,
+        const std::vector<BufferRole>& /*outputRoles*/, DeviceBuffer* /*deviceBuffer*/) {
+    return toAStatus(ErrorStatus::GENERAL_FAILURE, "InvalidDevice");
+}
+
+ndk::ScopedAStatus InvalidDevice::getCapabilities(Capabilities* capabilities) {
+    *capabilities = kCapabilities;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getNumberOfCacheFilesNeeded(
+        NumberOfCacheFiles* numberOfCacheFiles) {
+    *numberOfCacheFiles = kNumberOfCacheFiles;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getSupportedExtensions(std::vector<Extension>* extensions) {
+    *extensions = kExtensions;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getSupportedOperations(const Model& model,
+                                                         std::vector<bool>* supportedOperations) {
+    if (const auto result = utils::validate(model); !result.ok()) {
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+    }
+    *supportedOperations = std::vector<bool>(model.main.operations.size(), false);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getType(DeviceType* deviceType) {
+    *deviceType = kDeviceType;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::getVersionString(std::string* versionString) {
+    *versionString = kVersionString;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::prepareModel(
+        const Model& model, ExecutionPreference preference, Priority priority, int64_t deadline,
+        const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+        const std::vector<ndk::ScopedFileDescriptor>& dataCache, const std::vector<uint8_t>& token,
+        const std::shared_ptr<IPreparedModelCallback>& callback) {
+    if (callback.get() == nullptr) {
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+                         "invalid callback passed to InvalidDevice::prepareModel");
+    }
+    if (const auto result = utils::validate(model); !result.ok()) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+    }
+    if (const auto result = utils::validate(preference); !result.ok()) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+    }
+    if (const auto result = utils::validate(priority); !result.ok()) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT, result.error());
+    }
+    if (deadline < -1) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+                         "Invalid deadline " + std::to_string(deadline));
+    }
+    if (modelCache.size() != static_cast<size_t>(kNumberOfCacheFiles.numModelCache)) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+                         "Invalid modelCache, size = " + std::to_string(modelCache.size()));
+    }
+    if (dataCache.size() != static_cast<size_t>(kNumberOfCacheFiles.numDataCache)) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(ErrorStatus::INVALID_ARGUMENT,
+                         "Invalid modelCache, size = " + std::to_string(dataCache.size()));
+    }
+    if (token.size() != IDevice::BYTE_SIZE_OF_CACHE_TOKEN) {
+        callback->notify(ErrorStatus::INVALID_ARGUMENT, nullptr);
+        return toAStatus(
+                ErrorStatus::INVALID_ARGUMENT,
+                "Invalid cache token, size = " + std::to_string(IDevice::BYTE_SIZE_OF_CACHE_TOKEN));
+    }
+    callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus InvalidDevice::prepareModelFromCache(
+        int64_t /*deadline*/, const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+        const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+        const std::vector<uint8_t>& /*token*/,
+        const std::shared_ptr<IPreparedModelCallback>& callback) {
+    callback->notify(ErrorStatus::GENERAL_FAILURE, nullptr);
+    return toAStatus(ErrorStatus::GENERAL_FAILURE, "InvalidDevice");
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/utils/src/PreparedModel.cpp b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
new file mode 100644
index 0000000..f861d74
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/PreparedModel.cpp
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+
+#include "PreparedModel.h"
+
+#include "Burst.h"
+#include "Callbacks.h"
+#include "Conversions.h"
+#include "Execution.h"
+#include "ProtectCallback.h"
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+#include <android/binder_auto_utils.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/CommonUtils.h>
+#include <nnapi/hal/HandleError.h>
+
+#include <memory>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+// See hardware/interfaces/neuralnetworks/utils/README.md for more information on AIDL interface
+// lifetimes across processes and for protecting asynchronous calls across AIDL.
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+nn::GeneralResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> convertExecutionResults(
+        const std::vector<OutputShape>& outputShapes, const Timing& timing) {
+    return std::make_pair(NN_TRY(nn::convert(outputShapes)), NN_TRY(nn::convert(timing)));
+}
+
+nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> convertFencedExecutionResults(
+        ErrorStatus status, const aidl_hal::Timing& timingLaunched,
+        const aidl_hal::Timing& timingFenced) {
+    HANDLE_HAL_STATUS(status) << "fenced execution callback info failed with " << toString(status);
+    return std::make_pair(NN_TRY(nn::convert(timingLaunched)), NN_TRY(nn::convert(timingFenced)));
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const PreparedModel>> PreparedModel::create(
+        std::shared_ptr<aidl_hal::IPreparedModel> preparedModel) {
+    if (preparedModel == nullptr) {
+        return NN_ERROR()
+               << "aidl_hal::utils::PreparedModel::create must have non-null preparedModel";
+    }
+
+    return std::make_shared<const PreparedModel>(PrivateConstructorTag{}, std::move(preparedModel));
+}
+
+PreparedModel::PreparedModel(PrivateConstructorTag /*tag*/,
+                             std::shared_ptr<aidl_hal::IPreparedModel> preparedModel)
+    : kPreparedModel(std::move(preparedModel)) {}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> PreparedModel::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared =
+            NN_TRY(hal::utils::makeExecutionFailure(hal::utils::convertRequestFromPointerToShared(
+                    &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+                    &maybeRequestInShared, &relocation)));
+
+    const auto aidlRequest = NN_TRY(hal::utils::makeExecutionFailure(convert(requestInShared)));
+    const auto aidlMeasure = NN_TRY(hal::utils::makeExecutionFailure(convert(measure)));
+    const auto aidlDeadline = NN_TRY(hal::utils::makeExecutionFailure(convert(deadline)));
+    const auto aidlLoopTimeoutDuration =
+            NN_TRY(hal::utils::makeExecutionFailure(convert(loopTimeoutDuration)));
+    return executeInternal(aidlRequest, aidlMeasure, aidlDeadline, aidlLoopTimeoutDuration,
+                           relocation);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+PreparedModel::executeInternal(const Request& request, bool measure, int64_t deadline,
+                               int64_t loopTimeoutDuration,
+                               const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    ExecutionResult executionResult;
+    const auto ret = kPreparedModel->executeSynchronously(request, measure, deadline,
+                                                          loopTimeoutDuration, &executionResult);
+    HANDLE_ASTATUS(ret) << "executeSynchronously failed";
+    if (!executionResult.outputSufficientSize) {
+        auto canonicalOutputShapes =
+                nn::convert(executionResult.outputShapes).value_or(std::vector<nn::OutputShape>{});
+        return NN_ERROR(nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, std::move(canonicalOutputShapes))
+               << "execution failed with " << nn::ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+    }
+    auto [outputShapes, timing] = NN_TRY(hal::utils::makeExecutionFailure(
+            convertExecutionResults(executionResult.outputShapes, executionResult.timing)));
+
+    if (relocation.output) {
+        relocation.output->flush();
+    }
+    return std::make_pair(std::move(outputShapes), timing);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFenced(const nn::Request& request, const std::vector<nn::SyncFence>& waitFor,
+                             nn::MeasureTiming measure, const nn::OptionalTimePoint& deadline,
+                             const nn::OptionalDuration& loopTimeoutDuration,
+                             const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    const auto aidlRequest = NN_TRY(convert(requestInShared));
+    const auto aidlWaitFor = NN_TRY(convert(waitFor));
+    const auto aidlMeasure = NN_TRY(convert(measure));
+    const auto aidlDeadline = NN_TRY(convert(deadline));
+    const auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    const auto aidlTimeoutDurationAfterFence = NN_TRY(convert(timeoutDurationAfterFence));
+    return executeFencedInternal(aidlRequest, aidlWaitFor, aidlMeasure, aidlDeadline,
+                                 aidlLoopTimeoutDuration, aidlTimeoutDurationAfterFence,
+                                 relocation);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+PreparedModel::executeFencedInternal(const Request& request,
+                                     const std::vector<ndk::ScopedFileDescriptor>& waitFor,
+                                     bool measure, int64_t deadline, int64_t loopTimeoutDuration,
+                                     int64_t timeoutDurationAfterFence,
+                                     const hal::utils::RequestRelocation& relocation) const {
+    if (relocation.input) {
+        relocation.input->flush();
+    }
+
+    FencedExecutionResult result;
+    const auto ret =
+            kPreparedModel->executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+                                          timeoutDurationAfterFence, &result);
+    HANDLE_ASTATUS(ret) << "executeFenced failed";
+
+    auto resultSyncFence = nn::SyncFence::createAsSignaled();
+    if (result.syncFence.get() != -1) {
+        resultSyncFence = nn::SyncFence::create(NN_TRY(nn::convert(result.syncFence))).value();
+    }
+
+    auto callback = result.callback;
+    if (callback == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE) << "callback is null";
+    }
+
+    // If executeFenced required the request memory to be moved into shared memory, block here until
+    // the fenced execution has completed and flush the memory back.
+    if (relocation.output) {
+        const auto state = resultSyncFence.syncWait({});
+        if (state != nn::SyncFence::FenceState::SIGNALED) {
+            return NN_ERROR() << "syncWait failed with " << state;
+        }
+        relocation.output->flush();
+    }
+
+    // Create callback which can be used to retrieve the execution error status and timings.
+    nn::ExecuteFencedInfoCallback resultCallback =
+            [callback]() -> nn::GeneralResult<std::pair<nn::Timing, nn::Timing>> {
+        ErrorStatus errorStatus;
+        Timing timingLaunched;
+        Timing timingFenced;
+        const auto ret = callback->getExecutionInfo(&timingLaunched, &timingFenced, &errorStatus);
+        HANDLE_ASTATUS(ret) << "fenced execution callback getExecutionInfo failed";
+        return convertFencedExecutionResults(errorStatus, timingLaunched, timingFenced);
+    };
+
+    return std::make_pair(std::move(resultSyncFence), std::move(resultCallback));
+}
+
+nn::GeneralResult<nn::SharedExecution> PreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    // Ensure that request is ready for IPC.
+    std::optional<nn::Request> maybeRequestInShared;
+    hal::utils::RequestRelocation relocation;
+    const nn::Request& requestInShared = NN_TRY(hal::utils::convertRequestFromPointerToShared(
+            &request, nn::kDefaultRequestMemoryAlignment, nn::kDefaultRequestMemoryPadding,
+            &maybeRequestInShared, &relocation));
+
+    auto aidlRequest = NN_TRY(convert(requestInShared));
+    auto aidlMeasure = NN_TRY(convert(measure));
+    auto aidlLoopTimeoutDuration = NN_TRY(convert(loopTimeoutDuration));
+    return Execution::create(shared_from_this(), std::move(aidlRequest), std::move(relocation),
+                             aidlMeasure, aidlLoopTimeoutDuration);
+}
+
+nn::GeneralResult<nn::SharedBurst> PreparedModel::configureExecutionBurst() const {
+    std::shared_ptr<IBurst> burst;
+    const auto ret = kPreparedModel->configureExecutionBurst(&burst);
+    HANDLE_ASTATUS(ret) << "configureExecutionBurst failed";
+    return Burst::create(std::move(burst));
+}
+
+std::any PreparedModel::getUnderlyingResource() const {
+    std::shared_ptr<aidl_hal::IPreparedModel> resource = kPreparedModel;
+    return resource;
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/ProtectCallback.cpp b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
new file mode 100644
index 0000000..124641c
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/ProtectCallback.cpp
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#include "ProtectCallback.h"
+
+#include <android-base/logging.h>
+#include <android-base/scopeguard.h>
+#include <android-base/thread_annotations.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/Result.h>
+#include <nnapi/hal/ProtectCallback.h>
+
+#include <algorithm>
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include "Utils.h"
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+void DeathMonitor::serviceDied() {
+    std::lock_guard guard(mMutex);
+    std::for_each(mObjects.begin(), mObjects.end(),
+                  [](hal::utils::IProtectedCallback* killable) { killable->notifyAsDeadObject(); });
+}
+
+void DeathMonitor::serviceDied(void* cookie) {
+    auto deathMonitor = static_cast<DeathMonitor*>(cookie);
+    deathMonitor->serviceDied();
+}
+
+void DeathMonitor::add(hal::utils::IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    mObjects.push_back(killable);
+}
+
+void DeathMonitor::remove(hal::utils::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(std::shared_ptr<ndk::ICInterface> object) {
+    if (object == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::DeathHandler::create must have non-null object";
+    }
+    auto deathMonitor = std::make_shared<DeathMonitor>();
+    auto deathRecipient = ndk::ScopedAIBinder_DeathRecipient(
+            AIBinder_DeathRecipient_new(DeathMonitor::serviceDied));
+
+    // If passed a local binder, AIBinder_linkToDeath will do nothing and return
+    // STATUS_INVALID_OPERATION. We ignore this case because we only use local binders in tests
+    // where this is not an error.
+    if (object->isRemote()) {
+        const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_linkToDeath(
+                object->asBinder().get(), deathRecipient.get(), deathMonitor.get()));
+        HANDLE_ASTATUS(ret) << "AIBinder_linkToDeath failed";
+    }
+
+    return DeathHandler(std::move(object), std::move(deathRecipient), std::move(deathMonitor));
+}
+
+DeathHandler::DeathHandler(std::shared_ptr<ndk::ICInterface> object,
+                           ndk::ScopedAIBinder_DeathRecipient deathRecipient,
+                           std::shared_ptr<DeathMonitor> deathMonitor)
+    : kObject(std::move(object)),
+      kDeathRecipient(std::move(deathRecipient)),
+      kDeathMonitor(std::move(deathMonitor)) {
+    CHECK(kObject != nullptr);
+    CHECK(kDeathRecipient.get() != nullptr);
+    CHECK(kDeathMonitor != nullptr);
+}
+
+DeathHandler::~DeathHandler() {
+    if (kObject != nullptr && kDeathRecipient.get() != nullptr && kDeathMonitor != nullptr) {
+        const auto ret = ndk::ScopedAStatus::fromStatus(AIBinder_unlinkToDeath(
+                kObject->asBinder().get(), kDeathRecipient.get(), kDeathMonitor.get()));
+        const auto maybeSuccess = handleTransportError(ret);
+        if (!maybeSuccess.ok()) {
+            LOG(ERROR) << maybeSuccess.error().message;
+        }
+    }
+}
+
+[[nodiscard]] ::android::base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
+        hal::utils::IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    kDeathMonitor->add(killable);
+    return ::android::base::make_scope_guard(
+            [deathMonitor = kDeathMonitor, killable] { deathMonitor->remove(killable); });
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Service.cpp b/neuralnetworks/aidl/utils/src/Service.cpp
new file mode 100644
index 0000000..ac182a2
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Service.cpp
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+
+#include "Service.h"
+
+#include <AndroidVersionUtil.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <string>
+
+#include "Device.h"
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+nn::GeneralResult<nn::SharedDevice> getDevice(const std::string& instanceName) {
+    auto fullName = std::string(IDevice::descriptor) + "/" + instanceName;
+    hal::utils::ResilientDevice::Factory makeDevice =
+            [instanceName,
+             name = std::move(fullName)](bool blocking) -> nn::GeneralResult<nn::SharedDevice> {
+        std::add_pointer_t<AIBinder*(const char*)> getService;
+        if (blocking) {
+            if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
+                getService = AServiceManager_waitForService;
+            } else {
+                getService = AServiceManager_getService;
+            }
+        } else {
+            getService = AServiceManager_checkService;
+        }
+
+        auto service = IDevice::fromBinder(ndk::SpAIBinder(getService(name.c_str())));
+        if (service == nullptr) {
+            return NN_ERROR()
+                   << (blocking ? "AServiceManager_waitForService (or AServiceManager_getService)"
+                                : "AServiceManager_checkService")
+                   << " returned nullptr";
+        }
+        ABinderProcess_startThreadPool();
+        return Device::create(instanceName, std::move(service));
+    };
+
+    return hal::utils::ResilientDevice::create(std::move(makeDevice));
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/src/Utils.cpp b/neuralnetworks/aidl/utils/src/Utils.cpp
new file mode 100644
index 0000000..03407be
--- /dev/null
+++ b/neuralnetworks/aidl/utils/src/Utils.cpp
@@ -0,0 +1,146 @@
+/*
+ * 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.
+ */
+
+#include "Utils.h"
+
+#include <aidl/android/hardware/common/Ashmem.h>
+#include <aidl/android/hardware/common/MappableFile.h>
+#include <aidl/android/hardware/graphics/common/HardwareBuffer.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_status.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd);
+using utils::clone;
+
+template <typename Type>
+nn::GeneralResult<std::vector<Type>> cloneVec(const std::vector<Type>& arguments) {
+    std::vector<Type> clonedObjects;
+    clonedObjects.reserve(arguments.size());
+    for (const auto& argument : arguments) {
+        clonedObjects.push_back(NN_TRY(clone(argument)));
+    }
+    return clonedObjects;
+}
+
+template <typename Type>
+nn::GeneralResult<std::vector<Type>> clone(const std::vector<Type>& arguments) {
+    return cloneVec(arguments);
+}
+
+nn::GeneralResult<ndk::ScopedFileDescriptor> clone(const ndk::ScopedFileDescriptor& fd) {
+    auto duplicatedFd = NN_TRY(nn::dupFd(fd.get()));
+    return ndk::ScopedFileDescriptor(duplicatedFd.release());
+}
+
+nn::GeneralResult<common::NativeHandle> clone(const common::NativeHandle& handle) {
+    return common::NativeHandle{
+            .fds = NN_TRY(cloneVec(handle.fds)),
+            .ints = handle.ints,
+    };
+}
+
+}  // namespace
+
+nn::GeneralResult<Memory> clone(const Memory& memory) {
+    switch (memory.getTag()) {
+        case Memory::Tag::ashmem: {
+            const auto& ashmem = memory.get<Memory::Tag::ashmem>();
+            auto handle = common::Ashmem{
+                    .fd = NN_TRY(clone(ashmem.fd)),
+                    .size = ashmem.size,
+            };
+            return Memory::make<Memory::Tag::ashmem>(std::move(handle));
+        }
+        case Memory::Tag::mappableFile: {
+            const auto& memFd = memory.get<Memory::Tag::mappableFile>();
+            auto handle = common::MappableFile{
+                    .length = memFd.length,
+                    .prot = memFd.prot,
+                    .fd = NN_TRY(clone(memFd.fd)),
+                    .offset = memFd.offset,
+            };
+            return Memory::make<Memory::Tag::mappableFile>(std::move(handle));
+        }
+        case Memory::Tag::hardwareBuffer: {
+            const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>();
+            auto handle = graphics::common::HardwareBuffer{
+                    .description = hardwareBuffer.description,
+                    .handle = NN_TRY(clone(hardwareBuffer.handle)),
+            };
+            return Memory::make<Memory::Tag::hardwareBuffer>(std::move(handle));
+        }
+    }
+    return (NN_ERROR() << "Unrecognized Memory::Tag: " << memory.getTag())
+            .
+            operator nn::GeneralResult<Memory>();
+}
+
+nn::GeneralResult<RequestMemoryPool> clone(const RequestMemoryPool& requestPool) {
+    using Tag = RequestMemoryPool::Tag;
+    switch (requestPool.getTag()) {
+        case Tag::pool:
+            return RequestMemoryPool::make<Tag::pool>(NN_TRY(clone(requestPool.get<Tag::pool>())));
+        case Tag::token:
+            return RequestMemoryPool::make<Tag::token>(requestPool.get<Tag::token>());
+    }
+    // Using explicit type conversion because std::variant inside the RequestMemoryPool confuses the
+    // compiler.
+    return (NN_ERROR() << "Unrecognized request pool tag: " << requestPool.getTag())
+            .
+            operator nn::GeneralResult<RequestMemoryPool>();
+}
+
+nn::GeneralResult<Request> clone(const Request& request) {
+    return Request{
+            .inputs = request.inputs,
+            .outputs = request.outputs,
+            .pools = NN_TRY(clone(request.pools)),
+    };
+}
+
+nn::GeneralResult<Model> clone(const Model& model) {
+    return Model{
+            .main = model.main,
+            .referenced = model.referenced,
+            .operandValues = model.operandValues,
+            .pools = NN_TRY(clone(model.pools)),
+            .relaxComputationFloat32toFloat16 = model.relaxComputationFloat32toFloat16,
+            .extensionNameToPrefix = model.extensionNameToPrefix,
+    };
+}
+
+nn::GeneralResult<void> handleTransportError(const ndk::ScopedAStatus& ret) {
+    if (ret.getStatus() == STATUS_DEAD_OBJECT) {
+        return nn::error(nn::ErrorStatus::DEAD_OBJECT)
+               << "Binder transaction returned STATUS_DEAD_OBJECT: " << ret.getDescription();
+    }
+    if (ret.isOk()) {
+        return {};
+    }
+    if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
+        return nn::error(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Binder transaction returned exception: " << ret.getDescription();
+    }
+    return nn::error(static_cast<nn::ErrorStatus>(ret.getServiceSpecificError()))
+           << ret.getMessage();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/BufferTest.cpp b/neuralnetworks/aidl/utils/test/BufferTest.cpp
new file mode 100644
index 0000000..9736160
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/BufferTest.cpp
@@ -0,0 +1,212 @@
+/*
+ * 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.
+ */
+
+#include "MockBuffer.h"
+
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/IBuffer.h>
+#include <android/binder_auto_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/Buffer.h>
+
+#include <functional>
+#include <memory>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+const auto kMemory = nn::createSharedMemory(4).value();
+const std::shared_ptr<IBuffer> kInvalidBuffer;
+constexpr auto kInvalidToken = nn::Request::MemoryDomainToken{0};
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+constexpr auto makeGeneralFailure = [] {
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+}  // namespace
+
+TEST(BufferTest, invalidBuffer) {
+    // run test
+    const auto result = Buffer::create(kInvalidBuffer, kToken);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, invalidToken) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+
+    // run test
+    const auto result = Buffer::create(mockBuffer, kInvalidToken);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, create) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+
+    // run test
+    const auto token = buffer->getToken();
+
+    // verify result
+    EXPECT_EQ(token, kToken);
+}
+
+TEST(BufferTest, copyTo) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeStatusOk));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    EXPECT_TRUE(result.has_value()) << result.error().message;
+}
+
+TEST(BufferTest, copyToError) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToTransportFailure) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyToDeadObject) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = buffer->copyTo(kMemory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(BufferTest, copyFrom) {
+    // setup call
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(InvokeWithoutArgs(makeStatusOk));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value());
+}
+
+TEST(BufferTest, copyFromError) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromTransportFailure) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(BufferTest, copyFromDeadObject) {
+    // setup test
+    const auto mockBuffer = MockBuffer::create();
+    const auto buffer = Buffer::create(mockBuffer, kToken).value();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = buffer->copyFrom(kMemory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/DeviceTest.cpp b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
new file mode 100644
index 0000000..f121aca
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/DeviceTest.cpp
@@ -0,0 +1,876 @@
+/*
+ * 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.
+ */
+
+#include "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_status.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/Device.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+namespace nn = ::android::nn;
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::SetArgPointee;
+
+const nn::Model kSimpleModel = {
+        .main = {.operands = {{.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_INPUT},
+                              {.type = nn::OperandType::TENSOR_FLOAT32,
+                               .dimensions = {1},
+                               .lifetime = nn::Operand::LifeTime::SUBGRAPH_OUTPUT}},
+                 .operations = {{.type = nn::OperationType::RELU, .inputs = {0}, .outputs = {1}}},
+                 .inputIndexes = {0},
+                 .outputIndexes = {1}}};
+
+const std::string kName = "Google-MockV1";
+const std::string kInvalidName = "";
+const std::shared_ptr<BnDevice> kInvalidDevice;
+constexpr PerformanceInfo kNoPerformanceInfo = {.execTime = std::numeric_limits<float>::max(),
+                                                .powerUsage = std::numeric_limits<float>::max()};
+constexpr NumberOfCacheFiles kNumberOfCacheFiles = {.numModelCache = nn::kMaxNumberOfCacheFiles - 1,
+                                                    .numDataCache = nn::kMaxNumberOfCacheFiles};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+std::shared_ptr<MockDevice> createMockDevice() {
+    const auto mockDevice = MockDevice::create();
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getVersionString(_))
+            .WillByDefault(DoAll(SetArgPointee<0>(kName), InvokeWithoutArgs(makeStatusOk)));
+    ON_CALL(*mockDevice, getType(_))
+            .WillByDefault(
+                    DoAll(SetArgPointee<0>(DeviceType::OTHER), InvokeWithoutArgs(makeStatusOk)));
+    ON_CALL(*mockDevice, getSupportedExtensions(_))
+            .WillByDefault(DoAll(SetArgPointee<0>(std::vector<Extension>{}),
+                                 InvokeWithoutArgs(makeStatusOk)));
+    ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .WillByDefault(
+                    DoAll(SetArgPointee<0>(kNumberOfCacheFiles), InvokeWithoutArgs(makeStatusOk)));
+    ON_CALL(*mockDevice, getCapabilities(_))
+            .WillByDefault(
+                    DoAll(SetArgPointee<0>(Capabilities{
+                                  .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+                                  .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+                                  .ifPerformance = kNoPerformanceInfo,
+                                  .whilePerformance = kNoPerformanceInfo,
+                          }),
+                          InvokeWithoutArgs(makeStatusOk)));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getType(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+constexpr auto makePreparedModelReturnImpl =
+        [](ErrorStatus launchStatus, ErrorStatus returnStatus,
+           const std::shared_ptr<MockPreparedModel>& preparedModel,
+           const std::shared_ptr<IPreparedModelCallback>& cb) {
+            cb->notify(returnStatus, preparedModel);
+            if (launchStatus == ErrorStatus::NONE) {
+                return ndk::ScopedAStatus::ok();
+            }
+            return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int32_t>(launchStatus));
+        };
+
+auto makePreparedModelReturn(ErrorStatus launchStatus, ErrorStatus returnStatus,
+                             const std::shared_ptr<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const Model& /*model*/, ExecutionPreference /*preference*/,
+                   Priority /*priority*/, const int64_t& /*deadline*/,
+                   const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+                   const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+                   const std::vector<uint8_t>& /*token*/,
+                   const std::shared_ptr<IPreparedModelCallback>& cb) -> ndk::ScopedAStatus {
+        return makePreparedModelReturnImpl(launchStatus, returnStatus, preparedModel, cb);
+    };
+}
+
+auto makePreparedModelFromCacheReturn(ErrorStatus launchStatus, ErrorStatus returnStatus,
+                                      const std::shared_ptr<MockPreparedModel>& preparedModel) {
+    return [launchStatus, returnStatus, preparedModel](
+                   const int64_t& /*deadline*/,
+                   const std::vector<ndk::ScopedFileDescriptor>& /*modelCache*/,
+                   const std::vector<ndk::ScopedFileDescriptor>& /*dataCache*/,
+                   const std::vector<uint8_t>& /*token*/,
+                   const std::shared_ptr<IPreparedModelCallback>& cb) {
+        return makePreparedModelReturnImpl(launchStatus, returnStatus, preparedModel, cb);
+    };
+}
+
+constexpr auto makeGeneralFailure = [] {
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+}  // namespace
+
+TEST(DeviceTest, invalidName) {
+    // run test
+    const auto device = MockDevice::create();
+    const auto result = Device::create(kInvalidName, device);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, invalidDevice) {
+    // run test
+    const auto result = Device::create(kName, kInvalidDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(DeviceTest, getVersionStringError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getVersionStringDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getTypeError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1).WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getTypeDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getType(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getSupportedExtensionsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedExtensionsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeeded) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_TRUE(result.has_value());
+    constexpr auto kNumberOfCacheFilesPair = std::make_pair<uint32_t, uint32_t>(
+            kNumberOfCacheFiles.numModelCache, kNumberOfCacheFiles.numDataCache);
+    EXPECT_EQ(result.value()->getNumberOfCacheFilesNeeded(), kNumberOfCacheFilesPair);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, dataCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(DoAll(SetArgPointee<0>(NumberOfCacheFiles{
+                                    .numModelCache = nn::kMaxNumberOfCacheFiles + 1,
+                                    .numDataCache = nn::kMaxNumberOfCacheFiles}),
+                            InvokeWithoutArgs(makeStatusOk)));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, modelCacheFilesExceedsSpecifiedMax) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(DoAll(SetArgPointee<0>(NumberOfCacheFiles{
+                                    .numModelCache = nn::kMaxNumberOfCacheFiles,
+                                    .numDataCache = nn::kMaxNumberOfCacheFiles + 1}),
+                            InvokeWithoutArgs(makeStatusOk)));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getNumberOfCacheFilesNeededDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getCapabilitiesError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getCapabilitiesDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getCapabilities(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = Device::create(kName, mockDevice);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, getName) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto& name = device->getName();
+
+    // verify result
+    EXPECT_EQ(name, kName);
+}
+
+TEST(DeviceTest, getFeatureLevel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify result
+    EXPECT_EQ(featureLevel, nn::Version::ANDROID_S);
+}
+
+TEST(DeviceTest, getCachedData) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    EXPECT_CALL(*mockDevice, getVersionString(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getType(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getSupportedExtensions(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded(_)).Times(1);
+    EXPECT_CALL(*mockDevice, getCapabilities(_)).Times(1);
+
+    const auto result = Device::create(kName, mockDevice);
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& device = result.value();
+
+    // run test and verify results
+    EXPECT_EQ(device->getVersionString(), device->getVersionString());
+    EXPECT_EQ(device->getType(), device->getType());
+    EXPECT_EQ(device->getSupportedExtensions(), device->getSupportedExtensions());
+    EXPECT_EQ(device->getNumberOfCacheFilesNeeded(), device->getNumberOfCacheFilesNeeded());
+    EXPECT_EQ(device->getCapabilities(), device->getCapabilities());
+}
+
+TEST(DeviceTest, getSupportedOperations) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(DoAll(
+                    SetArgPointee<1>(std::vector<bool>(kSimpleModel.main.operations.size(), true)),
+                    InvokeWithoutArgs(makeStatusOk)));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& supportedOperations = result.value();
+    EXPECT_EQ(supportedOperations.size(), kSimpleModel.main.operations.size());
+    EXPECT_THAT(supportedOperations, Each(testing::IsTrue()));
+}
+
+TEST(DeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, getSupportedOperationsDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->getSupportedOperations(kSimpleModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModel) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+                                                     mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::GENERAL_FAILURE,
+                                                     ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelReturn(ErrorStatus::NONE,
+                                                     ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(
+                    Invoke(makePreparedModelReturn(ErrorStatus::NONE, ErrorStatus::NONE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&device]() {
+        DeathMonitor::serviceDied(device->getDeathMonitor());
+        return ndk::ScopedAStatus::ok();
+    };
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModel(kSimpleModel, nn::ExecutionPreference::DEFAULT,
+                                             nn::Priority::DEFAULT, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCache) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+                                                              mockPreparedModel)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, prepareModelFromCacheLaunchError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    ErrorStatus::GENERAL_FAILURE, ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheReturnError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(
+                    ErrorStatus::NONE, ErrorStatus::GENERAL_FAILURE, nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheNullptrError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makePreparedModelFromCacheReturn(ErrorStatus::NONE, ErrorStatus::NONE,
+                                                              nullptr)));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, prepareModelFromCacheDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, prepareModelFromCacheAsyncCrash) {
+    // setup test
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto ret = [&device]() {
+        DeathMonitor::serviceDied(device->getDeathMonitor());
+        return ndk::ScopedAStatus::ok();
+    };
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(ret));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(DeviceTest, allocate) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    const auto mockBuffer = DeviceBuffer{.buffer = MockBuffer::create(), .token = 1};
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(DoAll(SetArgPointee<4>(mockBuffer), InvokeWithoutArgs(makeStatusOk)));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(DeviceTest, allocateError) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateTransportFailure) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(DeviceTest, allocateDeadObject) {
+    // setup call
+    const auto mockDevice = createMockDevice();
+    const auto device = Device::create(kName, mockDevice).value();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/utils/test/MockBuffer.h b/neuralnetworks/aidl/utils/test/MockBuffer.h
new file mode 100644
index 0000000..f77fa86
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockBuffer.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
+
+#include <aidl/android/hardware/neuralnetworks/BnBuffer.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockBuffer final : public BnBuffer {
+  public:
+    static std::shared_ptr<MockBuffer> create();
+
+    MOCK_METHOD(ndk::ScopedAStatus, copyTo, (const Memory& dst), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, copyFrom,
+                (const Memory& src, const std::vector<int32_t>& dimensions), (override));
+};
+
+inline std::shared_ptr<MockBuffer> MockBuffer::create() {
+    return ndk::SharedRefBase::make<MockBuffer>();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/aidl/utils/test/MockBurst.h b/neuralnetworks/aidl/utils/test/MockBurst.h
new file mode 100644
index 0000000..5083bbd
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockBurst.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
+
+#include <aidl/android/hardware/neuralnetworks/BnBurst.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockBurst final : public BnBurst {
+  public:
+    MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
+                (const Request& request, const std::vector<int64_t>& memoryIdentifierTokens,
+                 bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration,
+                 ExecutionResult* executionResult),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, releaseMemoryResource, (int64_t memoryIdentifierToken),
+                (override));
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_BURST_H
diff --git a/neuralnetworks/aidl/utils/test/MockDevice.h b/neuralnetworks/aidl/utils/test/MockDevice.h
new file mode 100644
index 0000000..3a28d55
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockDevice.h
@@ -0,0 +1,67 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
+
+#include <aidl/android/hardware/neuralnetworks/BnDevice.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockDevice final : public BnDevice {
+  public:
+    static std::shared_ptr<MockDevice> create();
+
+    MOCK_METHOD(ndk::ScopedAStatus, allocate,
+                (const BufferDesc& desc, const std::vector<IPreparedModelParcel>& preparedModels,
+                 const std::vector<BufferRole>& inputRoles,
+                 const std::vector<BufferRole>& outputRoles, DeviceBuffer* deviceBuffer),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getCapabilities, (Capabilities * capabilities), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getNumberOfCacheFilesNeeded,
+                (NumberOfCacheFiles * numberOfCacheFiles), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getSupportedExtensions, (std::vector<Extension> * extensions),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getSupportedOperations,
+                (const Model& model, std::vector<bool>* supportedOperations), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getType, (DeviceType * deviceType), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, getVersionString, (std::string * version), (override));
+    MOCK_METHOD(ndk::ScopedAStatus, prepareModel,
+                (const Model& model, ExecutionPreference preference, Priority priority,
+                 int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                 const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                 const std::vector<uint8_t>& token,
+                 const std::shared_ptr<IPreparedModelCallback>& callback),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, prepareModelFromCache,
+                (int64_t deadline, const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                 const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                 const std::vector<uint8_t>& token,
+                 const std::shared_ptr<IPreparedModelCallback>& callback),
+                (override));
+};
+
+inline std::shared_ptr<MockDevice> MockDevice::create() {
+    return ndk::SharedRefBase::make<MockDevice>();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
new file mode 100644
index 0000000..06f9ea2
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockFencedExecutionCallback.h
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
+
+#include <aidl/android/hardware/neuralnetworks/BnFencedExecutionCallback.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockFencedExecutionCallback final : public BnFencedExecutionCallback {
+  public:
+    static std::shared_ptr<MockFencedExecutionCallback> create();
+
+    // V1_3 methods below.
+    MOCK_METHOD(ndk::ScopedAStatus, getExecutionInfo,
+                (Timing * timingLaunched, Timing* timingFenced, ErrorStatus* errorStatus),
+                (override));
+};
+
+inline std::shared_ptr<MockFencedExecutionCallback> MockFencedExecutionCallback::create() {
+    return ndk::SharedRefBase::make<MockFencedExecutionCallback>();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_FENCED_EXECUTION_CALLBACK_H
diff --git a/neuralnetworks/aidl/utils/test/MockPreparedModel.h b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
new file mode 100644
index 0000000..a4ae2b7
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/MockPreparedModel.h
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
+
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModel.h>
+#include <android/binder_interface_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/Status.h>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+
+class MockPreparedModel final : public BnPreparedModel {
+  public:
+    static std::shared_ptr<MockPreparedModel> create();
+
+    MOCK_METHOD(ndk::ScopedAStatus, executeSynchronously,
+                (const Request& request, bool measureTiming, int64_t deadline,
+                 int64_t loopTimeoutDuration, ExecutionResult* executionResult),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, executeFenced,
+                (const Request& request, const std::vector<ndk::ScopedFileDescriptor>& waitFor,
+                 bool measureTiming, int64_t deadline, int64_t loopTimeoutDuration,
+                 int64_t duration, FencedExecutionResult* fencedExecutionResult),
+                (override));
+    MOCK_METHOD(ndk::ScopedAStatus, configureExecutionBurst, (std::shared_ptr<IBurst> * burst),
+                (override));
+};
+
+inline std::shared_ptr<MockPreparedModel> MockPreparedModel::create() {
+    return ndk::SharedRefBase::make<MockPreparedModel>();
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_AIDL_UTILS_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
new file mode 100644
index 0000000..8bb5c90
--- /dev/null
+++ b/neuralnetworks/aidl/utils/test/PreparedModelTest.cpp
@@ -0,0 +1,557 @@
+/*
+ * 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.
+ */
+
+#include "MockBurst.h"
+#include "MockFencedExecutionCallback.h"
+#include "MockPreparedModel.h"
+
+#include <aidl/android/hardware/neuralnetworks/IFencedExecutionCallback.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/PreparedModel.h>
+
+#include <functional>
+#include <memory>
+
+namespace aidl::android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::InvokeWithoutArgs;
+using ::testing::SetArgPointee;
+
+const std::shared_ptr<IPreparedModel> kInvalidPreparedModel;
+constexpr auto kNoTiming = Timing{.timeOnDeviceNs = -1, .timeInDriverNs = -1};
+
+constexpr auto makeStatusOk = [] { return ndk::ScopedAStatus::ok(); };
+
+constexpr auto makeGeneralFailure = [] {
+    return ndk::ScopedAStatus::fromServiceSpecificError(
+            static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+};
+constexpr auto makeGeneralTransportFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_NO_MEMORY);
+};
+constexpr auto makeDeadObjectFailure = [] {
+    return ndk::ScopedAStatus::fromStatus(STATUS_DEAD_OBJECT);
+};
+
+auto makeFencedExecutionResult(const std::shared_ptr<MockFencedExecutionCallback>& callback) {
+    return [callback](const Request& /*request*/,
+                      const std::vector<ndk::ScopedFileDescriptor>& /*waitFor*/,
+                      bool /*measureTiming*/, int64_t /*deadline*/, int64_t /*loopTimeoutDuration*/,
+                      int64_t /*duration*/, FencedExecutionResult* fencedExecutionResult) {
+        *fencedExecutionResult = FencedExecutionResult{.callback = callback,
+                                                       .syncFence = ndk::ScopedFileDescriptor(-1)};
+        return ndk::ScopedAStatus::ok();
+    };
+}
+
+}  // namespace
+
+TEST(PreparedModelTest, invalidPreparedModel) {
+    // run test
+    const auto result = PreparedModel::create(kInvalidPreparedModel);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSync) {
+    // setup call
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockExecutionResult = ExecutionResult{
+            .outputSufficientSize = true,
+            .outputShapes = {},
+            .timing = kNoTiming,
+    };
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(
+                    DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    EXPECT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(PreparedModelTest, executeSyncError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeGeneralFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, executeFenced) {
+    // setup call
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(1)
+            .WillOnce(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                            SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& [syncFence, callback] = result.value();
+    EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+    ASSERT_NE(callback, nullptr);
+
+    // get results from callback
+    const auto callbackResult = callback();
+    ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code << ": "
+                                            << callbackResult.error().message;
+}
+
+TEST(PreparedModelTest, executeFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                                   SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
+                                   Invoke(makeStatusOk))));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    const auto& [syncFence, callback] = result.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, executeFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteSync) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockExecutionResult = ExecutionResult{
+            .outputSufficientSize = true,
+            .outputShapes = {},
+            .timing = kNoTiming,
+    };
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(
+                    DoAll(SetArgPointee<4>(mockExecutionResult), InvokeWithoutArgs(makeStatusOk)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->compute({});
+        EXPECT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeGeneralFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteSyncDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeSynchronously(_, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->compute({});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, reusableExecuteFenced) {
+    // setup call
+    const uint32_t kNumberOfComputations = 2;
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                                  SetArgPointee<2>(ErrorStatus::NONE), Invoke(makeStatusOk)));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(kNumberOfComputations)
+            .WillRepeatedly(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute repeatedly
+    for (uint32_t i = 0; i < kNumberOfComputations; i++) {
+        const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+        ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code
+                                               << ": " << computeResult.error().message;
+        const auto& [syncFence, callback] = computeResult.value();
+        EXPECT_EQ(syncFence.syncWait({}), nn::SyncFence::FenceState::SIGNALED);
+        ASSERT_NE(callback, nullptr);
+
+        // get results from callback
+        const auto callbackResult = callback();
+        ASSERT_TRUE(callbackResult.has_value()) << "Failed with " << callbackResult.error().code
+                                                << ": " << callbackResult.error().message;
+    }
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedCallbackError) {
+    // setup call
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    const auto mockCallback = MockFencedExecutionCallback::create();
+    EXPECT_CALL(*mockCallback, getExecutionInfo(_, _, _))
+            .Times(1)
+            .WillOnce(Invoke(DoAll(SetArgPointee<0>(kNoTiming), SetArgPointee<1>(kNoTiming),
+                                   SetArgPointee<2>(ErrorStatus::GENERAL_FAILURE),
+                                   Invoke(makeStatusOk))));
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Invoke(makeFencedExecutionResult(mockCallback)));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_TRUE(computeResult.has_value()) << "Failed with " << computeResult.error().code << ": "
+                                           << computeResult.error().message;
+    const auto& [syncFence, callback] = computeResult.value();
+    EXPECT_NE(syncFence.syncWait({}), nn::SyncFence::FenceState::ACTIVE);
+    ASSERT_NE(callback, nullptr);
+
+    // verify callback failure
+    const auto callbackResult = callback();
+    ASSERT_FALSE(callbackResult.has_value());
+    EXPECT_EQ(callbackResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, reusableExecuteFencedDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+
+    // create execution
+    const auto createResult = preparedModel->createReusableExecution({}, {}, {});
+    ASSERT_TRUE(createResult.has_value())
+            << "Failed with " << createResult.error().code << ": " << createResult.error().message;
+    ASSERT_NE(createResult.value(), nullptr);
+
+    // invoke compute
+    const auto computeResult = createResult.value()->computeFenced({}, {}, {});
+    ASSERT_FALSE(computeResult.has_value());
+    EXPECT_EQ(computeResult.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, configureExecutionBurst) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto mockBurst = ndk::SharedRefBase::make<MockBurst>();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+            .Times(1)
+            .WillOnce(DoAll(SetArgPointee<0>(mockBurst), Invoke(makeStatusOk)));
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_NE(result.value(), nullptr);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstError) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralFailure));
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstTransportFailure) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeGeneralTransportFailure));
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(PreparedModelTest, configureExecutionBurstDeadObject) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    EXPECT_CALL(*mockPreparedModel, configureExecutionBurst(_))
+            .Times(1)
+            .WillOnce(InvokeWithoutArgs(makeDeadObjectFailure));
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto result = preparedModel->configureExecutionBurst();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(PreparedModelTest, getUnderlyingResource) {
+    // setup test
+    const auto mockPreparedModel = MockPreparedModel::create();
+    const auto preparedModel = PreparedModel::create(mockPreparedModel).value();
+
+    // run test
+    const auto resource = preparedModel->getUnderlyingResource();
+
+    // verify resource
+    const std::shared_ptr<IPreparedModel>* maybeMock =
+            std::any_cast<std::shared_ptr<IPreparedModel>>(&resource);
+    ASSERT_NE(maybeMock, nullptr);
+    EXPECT_EQ(maybeMock->get(), mockPreparedModel.get());
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/aidl/vts/OWNERS b/neuralnetworks/aidl/vts/OWNERS
new file mode 100644
index 0000000..6719a5b
--- /dev/null
+++ b/neuralnetworks/aidl/vts/OWNERS
@@ -0,0 +1,12 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+mikie@google.com
+mks@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/aidl/vts/functional/Android.bp b/neuralnetworks/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..d5b150a
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Android.bp
@@ -0,0 +1,79 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalNeuralnetworksTargetTest",
+    defaults: [
+        "neuralnetworks_vts_functional_defaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "BasicTests.cpp",
+        "Callbacks.cpp",
+        "CompilationCachingTests.cpp",
+        "GeneratedTestHarness.cpp",
+        "MemoryDomainTests.cpp",
+        "QualityOfServiceTests.cpp",
+        "TestAssertions.cpp",
+        "TestMain.cpp",
+        "Utils.cpp",
+        "ValidateModel.cpp",
+        "ValidateRequest.cpp",
+        "VtsHalNeuralnetworks.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libnativewindow",
+        "libvndksupport",
+    ],
+    static_libs: [
+        "android.hardware.common-V2-ndk_platform",
+        "android.hardware.graphics.common-V2-ndk_platform",
+        "android.hardware.neuralnetworks-V1-ndk_platform",
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libaidlcommonsupport",
+        "libgmock",
+        "libhidlmemory",
+        "libneuralnetworks_generated_test_harness",
+        "libneuralnetworks_utils",
+        "libsync",
+        "neuralnetworks_utils_hal_aidl",
+    ],
+    whole_static_libs: [
+        "neuralnetworks_generated_V1_0_example",
+        "neuralnetworks_generated_V1_1_example",
+        "neuralnetworks_generated_V1_2_example",
+        "neuralnetworks_generated_V1_3_example",
+    ],
+    header_libs: [
+        "libbase_headers",
+        "libneuralnetworks_headers",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/neuralnetworks/aidl/vts/functional/AndroidTest.xml b/neuralnetworks/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..384d420
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalNeuralnetworksTargetTest.">
+    <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.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalNeuralnetworksTargetTest->/data/local/tmp/VtsHalNeuralnetworksTargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalNeuralnetworksTargetTest" />
+        <option name="native-test-timeout" value="20m" />
+    </test>
+</configuration>
diff --git a/neuralnetworks/aidl/vts/functional/BasicTests.cpp b/neuralnetworks/aidl/vts/functional/BasicTests.cpp
new file mode 100644
index 0000000..b2f4507
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/BasicTests.cpp
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
+#include <aidl/android/hardware/neuralnetworks/Capabilities.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <aidl/android/hardware/neuralnetworks/Operand.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/Priority.h>
+#include <android/binder_interface_utils.h>
+
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using implementation::PreparedModelCallback;
+
+// create device test
+TEST_P(NeuralNetworksAidlTest, CreateDevice) {}
+
+// initialization
+TEST_P(NeuralNetworksAidlTest, GetCapabilitiesTest) {
+    Capabilities capabilities;
+    const auto retStatus = kDevice->getCapabilities(&capabilities);
+    ASSERT_TRUE(retStatus.isOk());
+
+    auto isPositive = [](const PerformanceInfo& perf) {
+        return perf.execTime > 0.0f && perf.powerUsage > 0.0f;
+    };
+
+    EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceScalar));
+    EXPECT_TRUE(isPositive(capabilities.relaxedFloat32toFloat16PerformanceTensor));
+    const auto& opPerf = capabilities.operandPerformance;
+    EXPECT_TRUE(
+            std::all_of(opPerf.begin(), opPerf.end(),
+                        [isPositive](const OperandPerformance& a) { return isPositive(a.info); }));
+    EXPECT_TRUE(std::is_sorted(opPerf.begin(), opPerf.end(),
+                               [](const OperandPerformance& a, const OperandPerformance& b) {
+                                   return a.type < b.type;
+                               }));
+    EXPECT_TRUE(std::all_of(opPerf.begin(), opPerf.end(), [](const OperandPerformance& a) {
+        return a.type != OperandType::SUBGRAPH;
+    }));
+    EXPECT_TRUE(isPositive(capabilities.ifPerformance));
+    EXPECT_TRUE(isPositive(capabilities.whilePerformance));
+}
+
+// detect cycle
+TEST_P(NeuralNetworksAidlTest, CycleTest) {
+    // opnd0 = TENSOR_FLOAT32            // model input
+    // opnd1 = TENSOR_FLOAT32            // model input
+    // opnd2 = INT32                     // model input
+    // opnd3 = ADD(opnd0, opnd4, opnd2)
+    // opnd4 = ADD(opnd1, opnd3, opnd2)
+    // opnd5 = ADD(opnd4, opnd0, opnd2)  // model output
+    //
+    //            +-----+
+    //            |     |
+    //            v     |
+    // 3 = ADD(0, 4, 2) |
+    // |                |
+    // +----------+     |
+    //            |     |
+    //            v     |
+    // 4 = ADD(1, 3, 2) |
+    // |                |
+    // +----------------+
+    // |
+    // |
+    // +-------+
+    //         |
+    //         v
+    // 5 = ADD(4, 0, 2)
+
+    const std::vector<Operand> operands = {
+            {
+                    // operands[0]
+                    .type = OperandType::TENSOR_FLOAT32,
+                    .dimensions = {1},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                    // operands[1]
+                    .type = OperandType::TENSOR_FLOAT32,
+                    .dimensions = {1},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                    // operands[2]
+                    .type = OperandType::INT32,
+                    .dimensions = {},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                    // operands[3]
+                    .type = OperandType::TENSOR_FLOAT32,
+                    .dimensions = {1},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                    // operands[4]
+                    .type = OperandType::TENSOR_FLOAT32,
+                    .dimensions = {1},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::TEMPORARY_VARIABLE,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+            {
+                    // operands[5]
+                    .type = OperandType::TENSOR_FLOAT32,
+                    .dimensions = {1},
+                    .scale = 0.0f,
+                    .zeroPoint = 0,
+                    .lifetime = OperandLifeTime::SUBGRAPH_OUTPUT,
+                    .location = {.poolIndex = 0, .offset = 0, .length = 0},
+            },
+    };
+
+    const std::vector<Operation> operations = {
+            {.type = OperationType::ADD, .inputs = {0, 4, 2}, .outputs = {3}},
+            {.type = OperationType::ADD, .inputs = {1, 3, 2}, .outputs = {4}},
+            {.type = OperationType::ADD, .inputs = {4, 0, 2}, .outputs = {5}},
+    };
+
+    Subgraph subgraph = {
+            .operands = operands,
+            .operations = operations,
+            .inputIndexes = {0, 1, 2},
+            .outputIndexes = {5},
+    };
+    const Model model = {
+            .main = std::move(subgraph),
+            .referenced = {},
+            .operandValues = {},
+            .pools = {},
+    };
+
+    // ensure that getSupportedOperations() checks model validity
+    std::vector<bool> supportedOps;
+    const auto supportedOpsStatus = kDevice->getSupportedOperations(model, &supportedOps);
+    ASSERT_FALSE(supportedOpsStatus.isOk());
+    ASSERT_EQ(supportedOpsStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    ASSERT_EQ(static_cast<ErrorStatus>(supportedOpsStatus.getServiceSpecificError()),
+              ErrorStatus::INVALID_ARGUMENT);
+
+    // ensure that prepareModel() checks model validity
+    auto preparedModelCallback = ndk::SharedRefBase::make<PreparedModelCallback>();
+    auto prepareLaunchStatus =
+            kDevice->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority,
+                                  kNoDeadline, {}, {}, kEmptyCacheToken, preparedModelCallback);
+    //     Note that preparation can fail for reasons other than an
+    //     invalid model (invalid model should result in
+    //     INVALID_ARGUMENT) -- for example, perhaps not all
+    //     operations are supported, or perhaps the device hit some
+    //     kind of capacity limit.
+    ASSERT_FALSE(prepareLaunchStatus.isOk());
+    EXPECT_EQ(prepareLaunchStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    EXPECT_NE(static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError()),
+              ErrorStatus::NONE);
+
+    EXPECT_NE(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+    EXPECT_EQ(preparedModelCallback->getPreparedModel(), nullptr);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/Callbacks.cpp b/neuralnetworks/aidl/vts/functional/Callbacks.cpp
new file mode 100644
index 0000000..ca2bb48
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Callbacks.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "Callbacks"
+
+#include "Callbacks.h"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <limits>
+
+namespace aidl::android::hardware::neuralnetworks::implementation {
+
+ndk::ScopedAStatus PreparedModelCallback::notify(
+        ErrorStatus errorStatus, const std::shared_ptr<IPreparedModel>& preparedModel) {
+    {
+        std::lock_guard<std::mutex> hold(mMutex);
+        // quick-return if object has already been notified
+        if (mNotified) {
+            return ndk::ScopedAStatus::ok();
+        }
+        // store results and mark as notified
+        mErrorStatus = errorStatus;
+        mPreparedModel = preparedModel;
+        mNotified = true;
+    }
+    mCondition.notify_all();
+    return ndk::ScopedAStatus::ok();
+}
+
+void PreparedModelCallback::wait() const {
+    std::unique_lock<std::mutex> lock(mMutex);
+    mCondition.wait(lock, [this] { return mNotified; });
+}
+
+ErrorStatus PreparedModelCallback::getStatus() const {
+    wait();
+    return mErrorStatus;
+}
+
+std::shared_ptr<IPreparedModel> PreparedModelCallback::getPreparedModel() const {
+    wait();
+    return mPreparedModel;
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::implementation
diff --git a/neuralnetworks/aidl/vts/functional/Callbacks.h b/neuralnetworks/aidl/vts/functional/Callbacks.h
new file mode 100644
index 0000000..0eb4d5f
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Callbacks.h
@@ -0,0 +1,131 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_AIDL_CALLBACKS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_CALLBACKS_H
+
+#include <android-base/thread_annotations.h>
+#include <condition_variable>
+#include <mutex>
+
+#include <aidl/android/hardware/neuralnetworks/BnPreparedModelCallback.h>
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+
+/*
+ * The Callback classes are used internally by the NeuralNetworks runtime to
+ * synchronize between different threads. An asynchronous task is launched
+ * paired with a callback object. When a client thread requires the output being
+ * generated by the asynchronous task, the client thread can wait for the result
+ * and be blocked until it has completed. Any wait may safely be called
+ * concurrently, even on the same callback object. When the asynchronous task
+ * has finished its workload, it must immediately call "notify". If the
+ * asynchronous task has failed to launch, the function that tried to launch the
+ * asynchronous task must immediately call "notify". This "notify" call
+ * awakens any client threads waiting on the callback object.
+ *
+ * These classes exist to enable synchronization across AIDL. When
+ * synchronization is only required in the same process, consider using
+ * std::future, std::mutex, std::condition_variable, or std::experimental::latch
+ * instead.
+ */
+
+namespace aidl::android::hardware::neuralnetworks::implementation {
+
+/**
+ * The PreparedModelCallback class is used to receive the error status of
+ * preparing a model as well as the prepared model from a task executing
+ * asynchronously with respect to the runtime. If a calling thread calls wait
+ * or get* on a PreparedModelCallback object and the corresponding asynchronous
+ * task has not finished preparing the model, the calling thread will block
+ * until the asynchronous task has called notify.
+ *
+ * If the callback object is notified more than once, only the results of the
+ * first call to notify are used, and the results from subsequent calls are
+ * discarded.
+ *
+ * This callback object is passed as an argument to IDevice::prepareModel*.
+ */
+class PreparedModelCallback : public BnPreparedModelCallback {
+  public:
+    /**
+     * IPreparedModelCallback::notify marks the callback object with the return
+     * status of the asynchronous model preparation along with the prepared
+     * model, and allows all prior and future wait calls on the
+     * PreparedModelCallback object to proceed.
+     *
+     * IPreparedModelCallback::notify must be called on a given PreparedModelCallback object.
+     *
+     * If the callback object is notified more than once, only the results of
+     * the first call to notify are used, and the results from subsequent calls
+     * are discarded.
+     *
+     * @param status Error status returned from asynchronously preparing the
+     *     model; will be:
+     *     - NONE if the asynchronous preparation was successful
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if the input model is invalid
+     * @param preparedModel Returned model that has been prepared for execution,
+     *     nullptr if the model was unable to be prepared.
+     */
+    ndk::ScopedAStatus notify(ErrorStatus status,
+                              const std::shared_ptr<IPreparedModel>& preparedModel) override;
+
+    /**
+     * PreparedModelCallback::wait blocks until notify has been called on the
+     * callback object.
+     */
+    void wait() const;
+
+    /**
+     * Retrieves the error status returned from the asynchronous task launched
+     * by IDevice::prepareModel*. If IDevice::prepareModel* has not finished
+     * asynchronously preparing the model, this call will block until the
+     * asynchronous task notifies the object.
+     *
+     * @return status Error status returned from asynchronously preparing the
+     *     model; will be:
+     *     - NONE if the asynchronous preparation was successful
+     *     - DEVICE_UNAVAILABLE if driver is offline or busy
+     *     - GENERAL_FAILURE if there is an unspecified error
+     *     - INVALID_ARGUMENT if the input model is invalid
+     */
+    ErrorStatus getStatus() const;
+
+    /**
+     * Retrieves the model that has been prepared for execution from the
+     * asynchronous task launched by IDevice::prepareModel*. If
+     * IDevice::prepareModel* has not finished asynchronously preparing the
+     * model, this call will block until the asynchronous task notifies the
+     * object.
+     *
+     * @return preparedModel Returned model that has been prepared for
+     *     execution, nullptr if the model was unable to be prepared.
+     */
+    std::shared_ptr<IPreparedModel> getPreparedModel() const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    bool mNotified GUARDED_BY(mMutex) = false;
+    ErrorStatus mErrorStatus = ErrorStatus::GENERAL_FAILURE;
+    std::shared_ptr<IPreparedModel> mPreparedModel;
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks::implementation
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_AIDL_CALLBACKS_H
diff --git a/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
new file mode 100644
index 0000000..94ce5c1
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/CompilationCachingTests.cpp
@@ -0,0 +1,1178 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <fcntl.h>
+#include <ftw.h>
+#include <gtest/gtest.h>
+#include <hidlmemory/mapping.h>
+#include <unistd.h>
+
+#include <cstdio>
+#include <cstdlib>
+#include <iterator>
+#include <random>
+#include <thread>
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "MemoryUtils.h"
+#include "TestHarness.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+// Forward declaration of the mobilenet generated test models in
+// frameworks/ml/nn/runtime/test/generated/.
+namespace generated_tests::mobilenet_224_gender_basic_fixed {
+const test_helper::TestModel& get_test_model();
+}  // namespace generated_tests::mobilenet_224_gender_basic_fixed
+
+namespace generated_tests::mobilenet_quantized {
+const test_helper::TestModel& get_test_model();
+}  // namespace generated_tests::mobilenet_quantized
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using namespace test_helper;
+using implementation::PreparedModelCallback;
+
+namespace float32_model {
+
+constexpr auto get_test_model = generated_tests::mobilenet_224_gender_basic_fixed::get_test_model;
+
+}  // namespace float32_model
+
+namespace quant8_model {
+
+constexpr auto get_test_model = generated_tests::mobilenet_quantized::get_test_model;
+
+}  // namespace quant8_model
+
+namespace {
+
+enum class AccessMode { READ_WRITE, READ_ONLY, WRITE_ONLY };
+
+// Creates cache handles based on provided file groups.
+// The outer vector corresponds to handles and the inner vector is for fds held by each handle.
+void createCacheFds(const std::vector<std::string>& files, const std::vector<AccessMode>& mode,
+                    std::vector<ndk::ScopedFileDescriptor>* fds) {
+    fds->clear();
+    fds->reserve(files.size());
+    for (uint32_t i = 0; i < files.size(); i++) {
+        const auto& file = files[i];
+        int fd;
+        if (mode[i] == AccessMode::READ_ONLY) {
+            fd = open(file.c_str(), O_RDONLY);
+        } else if (mode[i] == AccessMode::WRITE_ONLY) {
+            fd = open(file.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+        } else if (mode[i] == AccessMode::READ_WRITE) {
+            fd = open(file.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
+        } else {
+            FAIL();
+        }
+        ASSERT_GE(fd, 0);
+        fds->emplace_back(fd);
+    }
+}
+
+void createCacheFds(const std::vector<std::string>& files, AccessMode mode,
+                    std::vector<ndk::ScopedFileDescriptor>* fds) {
+    createCacheFds(files, std::vector<AccessMode>(files.size(), mode), fds);
+}
+
+// Create a chain of broadcast operations. The second operand is always constant tensor [1].
+// For simplicity, activation scalar is shared. The second operand is not shared
+// in the model to let driver maintain a non-trivial size of constant data and the corresponding
+// data locations in cache.
+//
+//                --------- activation --------
+//                ↓      ↓      ↓             ↓
+// E.g. input -> ADD -> ADD -> ADD -> ... -> ADD -> output
+//                ↑      ↑      ↑             ↑
+//               [1]    [1]    [1]           [1]
+//
+// This function assumes the operation is either ADD or MUL.
+template <typename CppType, TestOperandType operandType>
+TestModel createLargeTestModelImpl(TestOperationType op, uint32_t len) {
+    EXPECT_TRUE(op == TestOperationType::ADD || op == TestOperationType::MUL);
+
+    // Model operations and operands.
+    std::vector<TestOperation> operations(len);
+    std::vector<TestOperand> operands(len * 2 + 2);
+
+    // The activation scalar, value = 0.
+    operands[0] = {
+            .type = TestOperandType::INT32,
+            .dimensions = {},
+            .numberOfConsumers = len,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+            .data = TestBuffer::createFromVector<int32_t>({0}),
+    };
+
+    // The buffer value of the constant second operand. The logical value is always 1.0f.
+    CppType bufferValue;
+    // The scale of the first and second operand.
+    float scale1, scale2;
+    if (operandType == TestOperandType::TENSOR_FLOAT32) {
+        bufferValue = 1.0f;
+        scale1 = 0.0f;
+        scale2 = 0.0f;
+    } else if (op == TestOperationType::ADD) {
+        bufferValue = 1;
+        scale1 = 1.0f;
+        scale2 = 1.0f;
+    } else {
+        // To satisfy the constraint on quant8 MUL: input0.scale * input1.scale < output.scale,
+        // set input1 to have scale = 0.5f and bufferValue = 2, i.e. 1.0f in floating point.
+        bufferValue = 2;
+        scale1 = 1.0f;
+        scale2 = 0.5f;
+    }
+
+    for (uint32_t i = 0; i < len; i++) {
+        const uint32_t firstInputIndex = i * 2 + 1;
+        const uint32_t secondInputIndex = firstInputIndex + 1;
+        const uint32_t outputIndex = secondInputIndex + 1;
+
+        // The first operation input.
+        operands[firstInputIndex] = {
+                .type = operandType,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = scale1,
+                .zeroPoint = 0,
+                .lifetime = (i == 0 ? TestOperandLifeTime::MODEL_INPUT
+                                    : TestOperandLifeTime::TEMPORARY_VARIABLE),
+                .data = (i == 0 ? TestBuffer::createFromVector<CppType>({1}) : TestBuffer()),
+        };
+
+        // The second operation input, value = 1.
+        operands[secondInputIndex] = {
+                .type = operandType,
+                .dimensions = {1},
+                .numberOfConsumers = 1,
+                .scale = scale2,
+                .zeroPoint = 0,
+                .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+                .data = TestBuffer::createFromVector<CppType>({bufferValue}),
+        };
+
+        // The operation. All operations share the same activation scalar.
+        // The output operand is created as an input in the next iteration of the loop, in the case
+        // of all but the last member of the chain; and after the loop as a model output, in the
+        // case of the last member of the chain.
+        operations[i] = {
+                .type = op,
+                .inputs = {firstInputIndex, secondInputIndex, /*activation scalar*/ 0},
+                .outputs = {outputIndex},
+        };
+    }
+
+    // For TestOperationType::ADD, output = 1 + 1 * len = len + 1
+    // For TestOperationType::MUL, output = 1 * 1 ^ len = 1
+    CppType outputResult = static_cast<CppType>(op == TestOperationType::ADD ? len + 1u : 1u);
+
+    // The model output.
+    operands.back() = {
+            .type = operandType,
+            .dimensions = {1},
+            .numberOfConsumers = 0,
+            .scale = scale1,
+            .zeroPoint = 0,
+            .lifetime = TestOperandLifeTime::MODEL_OUTPUT,
+            .data = TestBuffer::createFromVector<CppType>({outputResult}),
+    };
+
+    return {
+            .main = {.operands = std::move(operands),
+                     .operations = std::move(operations),
+                     .inputIndexes = {1},
+                     .outputIndexes = {len * 2 + 1}},
+            .isRelaxed = false,
+    };
+}
+
+}  // namespace
+
+// Tag for the compilation caching tests.
+class CompilationCachingTestBase : public testing::Test {
+  protected:
+    CompilationCachingTestBase(std::shared_ptr<IDevice> device, OperandType type)
+        : kDevice(std::move(device)), kOperandType(type) {}
+
+    void SetUp() override {
+        testing::Test::SetUp();
+        ASSERT_NE(kDevice.get(), nullptr);
+
+        // Create cache directory. The cache directory and a temporary cache file is always created
+        // to test the behavior of prepareModelFromCache, even when caching is not supported.
+        char cacheDirTemp[] = "/data/local/tmp/TestCompilationCachingXXXXXX";
+        char* cacheDir = mkdtemp(cacheDirTemp);
+        ASSERT_NE(cacheDir, nullptr);
+        mCacheDir = cacheDir;
+        mCacheDir.push_back('/');
+
+        NumberOfCacheFiles numCacheFiles;
+        const auto ret = kDevice->getNumberOfCacheFilesNeeded(&numCacheFiles);
+        ASSERT_TRUE(ret.isOk());
+
+        mNumModelCache = numCacheFiles.numModelCache;
+        mNumDataCache = numCacheFiles.numDataCache;
+        ASSERT_GE(mNumModelCache, 0) << "Invalid numModelCache: " << mNumModelCache;
+        ASSERT_GE(mNumDataCache, 0) << "Invalid numDataCache: " << mNumDataCache;
+        mIsCachingSupported = mNumModelCache > 0 || mNumDataCache > 0;
+
+        // Create empty cache files.
+        mTmpCache = mCacheDir + "tmp";
+        for (uint32_t i = 0; i < mNumModelCache; i++) {
+            mModelCache.push_back({mCacheDir + "model" + std::to_string(i)});
+        }
+        for (uint32_t i = 0; i < mNumDataCache; i++) {
+            mDataCache.push_back({mCacheDir + "data" + std::to_string(i)});
+        }
+        // Placeholder handles, use AccessMode::WRITE_ONLY for createCacheFds to create files.
+        std::vector<ndk::ScopedFileDescriptor> modelHandle, dataHandle, tmpHandle;
+        createCacheFds(mModelCache, AccessMode::WRITE_ONLY, &modelHandle);
+        createCacheFds(mDataCache, AccessMode::WRITE_ONLY, &dataHandle);
+        createCacheFds({mTmpCache}, AccessMode::WRITE_ONLY, &tmpHandle);
+
+        if (!mIsCachingSupported) {
+            LOG(INFO) << "NN VTS: Early termination of test because vendor service does not "
+                         "support compilation caching.";
+            std::cout << "[          ]   Early termination of test because vendor service does not "
+                         "support compilation caching."
+                      << std::endl;
+        }
+    }
+
+    void TearDown() override {
+        // If the test passes, remove the tmp directory.  Otherwise, keep it for debugging purposes.
+        if (!testing::Test::HasFailure()) {
+            // Recursively remove the cache directory specified by mCacheDir.
+            auto callback = [](const char* entry, const struct stat*, int, struct FTW*) {
+                return remove(entry);
+            };
+            nftw(mCacheDir.c_str(), callback, 128, FTW_DEPTH | FTW_MOUNT | FTW_PHYS);
+        }
+        testing::Test::TearDown();
+    }
+
+    // Model and examples creators. According to kOperandType, the following methods will return
+    // either float32 model/examples or the quant8 variant.
+    TestModel createTestModel() {
+        if (kOperandType == OperandType::TENSOR_FLOAT32) {
+            return float32_model::get_test_model();
+        } else {
+            return quant8_model::get_test_model();
+        }
+    }
+
+    TestModel createLargeTestModel(OperationType op, uint32_t len) {
+        if (kOperandType == OperandType::TENSOR_FLOAT32) {
+            return createLargeTestModelImpl<float, TestOperandType::TENSOR_FLOAT32>(
+                    static_cast<TestOperationType>(op), len);
+        } else {
+            return createLargeTestModelImpl<uint8_t, TestOperandType::TENSOR_QUANT8_ASYMM>(
+                    static_cast<TestOperationType>(op), len);
+        }
+    }
+
+    // See if the service can handle the model.
+    bool isModelFullySupported(const Model& model) {
+        std::vector<bool> supportedOps;
+        const auto supportedCall = kDevice->getSupportedOperations(model, &supportedOps);
+        EXPECT_TRUE(supportedCall.isOk());
+        EXPECT_EQ(supportedOps.size(), model.main.operations.size());
+        if (!supportedCall.isOk() || supportedOps.size() != model.main.operations.size()) {
+            return false;
+        }
+        return std::all_of(supportedOps.begin(), supportedOps.end(),
+                           [](bool valid) { return valid; });
+    }
+
+    void saveModelToCache(const Model& model,
+                          const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                          const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                          std::shared_ptr<IPreparedModel>* preparedModel = nullptr) {
+        if (preparedModel != nullptr) *preparedModel = nullptr;
+
+        // Launch prepare model.
+        std::shared_ptr<PreparedModelCallback> preparedModelCallback =
+                ndk::SharedRefBase::make<PreparedModelCallback>();
+        std::vector<uint8_t> cacheToken(std::begin(mToken), std::end(mToken));
+        const auto prepareLaunchStatus = kDevice->prepareModel(
+                model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority, kNoDeadline,
+                modelCache, dataCache, cacheToken, preparedModelCallback);
+        ASSERT_TRUE(prepareLaunchStatus.isOk());
+
+        // Retrieve prepared model.
+        preparedModelCallback->wait();
+        ASSERT_EQ(preparedModelCallback->getStatus(), ErrorStatus::NONE);
+        if (preparedModel != nullptr) {
+            *preparedModel = preparedModelCallback->getPreparedModel();
+        }
+    }
+
+    bool checkEarlyTermination(ErrorStatus status) {
+        if (status == ErrorStatus::GENERAL_FAILURE) {
+            LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                         "save the prepared model that it does not support.";
+            std::cout << "[          ]   Early termination of test because vendor service cannot "
+                         "save the prepared model that it does not support."
+                      << std::endl;
+            return true;
+        }
+        return false;
+    }
+
+    bool checkEarlyTermination(const Model& model) {
+        if (!isModelFullySupported(model)) {
+            LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                         "prepare model that it does not support.";
+            std::cout << "[          ]   Early termination of test because vendor service cannot "
+                         "prepare model that it does not support."
+                      << std::endl;
+            return true;
+        }
+        return false;
+    }
+
+    // If fallbackModel is not provided, call prepareModelFromCache.
+    // If fallbackModel is provided, and prepareModelFromCache returns GENERAL_FAILURE,
+    // then prepareModel(fallbackModel) will be called.
+    // This replicates the behaviour of the runtime when loading a model from cache.
+    // NNAPI Shim depends on this behaviour and may try to load the model from cache in
+    // prepareModel (shim needs model information when loading from cache).
+    void prepareModelFromCache(const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                               const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                               std::shared_ptr<IPreparedModel>* preparedModel, ErrorStatus* status,
+                               const Model* fallbackModel = nullptr) {
+        // Launch prepare model from cache.
+        std::shared_ptr<PreparedModelCallback> preparedModelCallback =
+                ndk::SharedRefBase::make<PreparedModelCallback>();
+        std::vector<uint8_t> cacheToken(std::begin(mToken), std::end(mToken));
+        auto prepareLaunchStatus = kDevice->prepareModelFromCache(
+                kNoDeadline, modelCache, dataCache, cacheToken, preparedModelCallback);
+
+        // The shim does not support prepareModelFromCache() properly, but it
+        // will still attempt to create a model from cache when modelCache or
+        // dataCache is provided in prepareModel(). Instead of failing straight
+        // away, we try to utilize that other code path when fallbackModel is
+        // set. Note that we cannot verify whether the returned model was
+        // actually prepared from cache in that case.
+        if (!prepareLaunchStatus.isOk() &&
+            prepareLaunchStatus.getExceptionCode() == EX_SERVICE_SPECIFIC &&
+            static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError()) ==
+                    ErrorStatus::GENERAL_FAILURE &&
+            mIsCachingSupported && fallbackModel != nullptr) {
+            preparedModelCallback = ndk::SharedRefBase::make<PreparedModelCallback>();
+            prepareLaunchStatus = kDevice->prepareModel(
+                    *fallbackModel, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority,
+                    kNoDeadline, modelCache, dataCache, cacheToken, preparedModelCallback);
+        }
+
+        ASSERT_TRUE(prepareLaunchStatus.isOk() ||
+                    prepareLaunchStatus.getExceptionCode() == EX_SERVICE_SPECIFIC)
+                << "prepareLaunchStatus: " << prepareLaunchStatus.getDescription();
+        if (!prepareLaunchStatus.isOk()) {
+            *preparedModel = nullptr;
+            *status = static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError());
+            return;
+        }
+
+        // Retrieve prepared model.
+        preparedModelCallback->wait();
+        *status = preparedModelCallback->getStatus();
+        *preparedModel = preparedModelCallback->getPreparedModel();
+    }
+
+    // Replicate behaviour of runtime when loading model from cache.
+    // Test if prepareModelFromCache behaves correctly when faced with bad
+    // arguments. If prepareModelFromCache is not supported (GENERAL_FAILURE),
+    // it attempts to call prepareModel with same arguments, which is expected either
+    // to not support the model (GENERAL_FAILURE) or return a valid model.
+    void verifyModelPreparationBehaviour(const std::vector<ndk::ScopedFileDescriptor>& modelCache,
+                                         const std::vector<ndk::ScopedFileDescriptor>& dataCache,
+                                         const Model* model, const TestModel& testModel) {
+        std::shared_ptr<IPreparedModel> preparedModel;
+        ErrorStatus status;
+
+        // Verify that prepareModelFromCache fails either due to bad
+        // arguments (INVALID_ARGUMENT) or GENERAL_FAILURE if not supported.
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                              /*fallbackModel=*/nullptr);
+        if (status != ErrorStatus::INVALID_ARGUMENT) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        }
+        ASSERT_EQ(preparedModel, nullptr);
+
+        // If caching is not supported, attempt calling prepareModel.
+        if (status == ErrorStatus::GENERAL_FAILURE) {
+            // Fallback with prepareModel should succeed regardless of cache files
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                                  /*fallbackModel=*/model);
+            // Unless caching is not supported?
+            if (status != ErrorStatus::GENERAL_FAILURE) {
+                // But if it is, we should see a valid model.
+                ASSERT_EQ(status, ErrorStatus::NONE);
+                ASSERT_NE(preparedModel, nullptr);
+                EvaluatePreparedModel(kDevice, preparedModel, testModel,
+                                      /*testKind=*/TestKind::GENERAL);
+            }
+        }
+    }
+
+    // Absolute path to the temporary cache directory.
+    std::string mCacheDir;
+
+    // Groups of file paths for model and data cache in the tmp cache directory, initialized with
+    // size = mNum{Model|Data}Cache. The outer vector corresponds to handles and the inner vector is
+    // for fds held by each handle.
+    std::vector<std::string> mModelCache;
+    std::vector<std::string> mDataCache;
+
+    // A separate temporary file path in the tmp cache directory.
+    std::string mTmpCache;
+
+    uint8_t mToken[static_cast<uint32_t>(IDevice::BYTE_SIZE_OF_CACHE_TOKEN)] = {};
+    uint32_t mNumModelCache;
+    uint32_t mNumDataCache;
+    bool mIsCachingSupported;
+
+    const std::shared_ptr<IDevice> kDevice;
+    // The primary data type of the testModel.
+    const OperandType kOperandType;
+};
+
+using CompilationCachingTestParam = std::tuple<NamedDevice, OperandType>;
+
+// A parameterized fixture of CompilationCachingTestBase. Every test will run twice, with the first
+// pass running with float32 models and the second pass running with quant8 models.
+class CompilationCachingTest : public CompilationCachingTestBase,
+                               public testing::WithParamInterface<CompilationCachingTestParam> {
+  protected:
+    CompilationCachingTest()
+        : CompilationCachingTestBase(getData(std::get<NamedDevice>(GetParam())),
+                                     std::get<OperandType>(GetParam())) {}
+};
+
+TEST_P(CompilationCachingTest, CacheSavingAndRetrieval) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+    std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+
+    // Save the compilation to cache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(model, modelCache, dataCache);
+    }
+
+    // Retrieve preparedModel from cache.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                              /*fallbackModel=*/&model);
+        if (!mIsCachingSupported) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else if (checkEarlyTermination(status)) {
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+            ASSERT_NE(preparedModel, nullptr);
+        }
+    }
+
+    // Execute and verify results.
+    EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+}
+
+TEST_P(CompilationCachingTest, CacheSavingAndRetrievalNonZeroOffset) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+    std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+
+    // Save the compilation to cache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        uint8_t placeholderBytes[] = {0, 0};
+        // Write a placeholder integer to the cache.
+        // The driver should be able to handle non-empty cache and non-zero fd offset.
+        for (uint32_t i = 0; i < modelCache.size(); i++) {
+            ASSERT_EQ(write(modelCache[i].get(), &placeholderBytes, sizeof(placeholderBytes)),
+                      sizeof(placeholderBytes));
+        }
+        for (uint32_t i = 0; i < dataCache.size(); i++) {
+            ASSERT_EQ(write(dataCache[i].get(), &placeholderBytes, sizeof(placeholderBytes)),
+                      sizeof(placeholderBytes));
+        }
+        saveModelToCache(model, modelCache, dataCache);
+    }
+
+    // Retrieve preparedModel from cache.
+    {
+        preparedModel = nullptr;
+        ErrorStatus status;
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        uint8_t placeholderByte = 0;
+        // Advance the offset of each handle by one byte.
+        // The driver should be able to handle non-zero fd offset.
+        for (uint32_t i = 0; i < modelCache.size(); i++) {
+            ASSERT_GE(read(modelCache[i].get(), &placeholderByte, 1), 0);
+        }
+        for (uint32_t i = 0; i < dataCache.size(); i++) {
+            ASSERT_GE(read(dataCache[i].get(), &placeholderByte, 1), 0);
+        }
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                              /*fallbackModel=*/&model);
+        if (!mIsCachingSupported) {
+            ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else if (checkEarlyTermination(status)) {
+            ASSERT_EQ(preparedModel, nullptr);
+            return;
+        } else {
+            ASSERT_EQ(status, ErrorStatus::NONE);
+            ASSERT_NE(preparedModel, nullptr);
+        }
+    }
+
+    // Execute and verify results.
+    EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+}
+
+TEST_P(CompilationCachingTest, SaveToCacheInvalidNumCache) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+
+    // Test with number of model cache files greater than mNumModelCache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        // Pass an additional cache file for model cache.
+        mModelCache.push_back({mTmpCache});
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mModelCache.pop_back();
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of model cache files smaller than mNumModelCache.
+    if (mModelCache.size() > 0) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        // Pop out the last cache file.
+        auto tmp = mModelCache.back();
+        mModelCache.pop_back();
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mModelCache.push_back(tmp);
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of data cache files greater than mNumDataCache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        // Pass an additional cache file for data cache.
+        mDataCache.push_back({mTmpCache});
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mDataCache.pop_back();
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of data cache files smaller than mNumDataCache.
+    if (mDataCache.size() > 0) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        // Pop out the last cache file.
+        auto tmp = mDataCache.back();
+        mDataCache.pop_back();
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mDataCache.push_back(tmp);
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+}
+
+TEST_P(CompilationCachingTest, PrepareModelFromCacheInvalidNumCache) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+
+    // Save the compilation to cache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(model, modelCache, dataCache);
+    }
+
+    // Test with number of model cache files greater than mNumModelCache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        mModelCache.push_back({mTmpCache});
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mModelCache.pop_back();
+
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of model cache files smaller than mNumModelCache.
+    if (mModelCache.size() > 0) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        auto tmp = mModelCache.back();
+        mModelCache.pop_back();
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mModelCache.push_back(tmp);
+
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of data cache files greater than mNumDataCache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        mDataCache.push_back({mTmpCache});
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mDataCache.pop_back();
+
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Test with number of data cache files smaller than mNumDataCache.
+    if (mDataCache.size() > 0) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        auto tmp = mDataCache.back();
+        mDataCache.pop_back();
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        mDataCache.push_back(tmp);
+
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+}
+
+TEST_P(CompilationCachingTest, SaveToCacheInvalidAccessMode) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+    std::vector<AccessMode> modelCacheMode(mNumModelCache, AccessMode::READ_WRITE);
+    std::vector<AccessMode> dataCacheMode(mNumDataCache, AccessMode::READ_WRITE);
+
+    // Go through each handle in model cache, test with invalid access mode.
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        modelCacheMode[i] = AccessMode::READ_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        modelCacheMode[i] = AccessMode::READ_WRITE;
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Go through each handle in data cache, test with invalid access mode.
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        dataCacheMode[i] = AccessMode::READ_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        dataCacheMode[i] = AccessMode::READ_WRITE;
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        saveModelToCache(model, modelCache, dataCache, &preparedModel);
+        ASSERT_NE(preparedModel, nullptr);
+        // Execute and verify results.
+        EvaluatePreparedModel(kDevice, preparedModel, testModel, /*testKind=*/TestKind::GENERAL);
+        // Check if prepareModelFromCache fails.
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+}
+
+TEST_P(CompilationCachingTest, PrepareModelFromCacheInvalidAccessMode) {
+    // Create test HIDL model and compile.
+    const TestModel& testModel = createTestModel();
+    const Model model = createModel(testModel);
+    if (checkEarlyTermination(model)) return;
+    std::vector<AccessMode> modelCacheMode(mNumModelCache, AccessMode::READ_WRITE);
+    std::vector<AccessMode> dataCacheMode(mNumDataCache, AccessMode::READ_WRITE);
+
+    // Save the compilation to cache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(model, modelCache, dataCache);
+    }
+
+    // Go through each handle in model cache, test with invalid access mode.
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        modelCacheMode[i] = AccessMode::WRITE_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        modelCacheMode[i] = AccessMode::READ_WRITE;
+
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+
+    // Go through each handle in data cache, test with invalid access mode.
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        dataCacheMode[i] = AccessMode::WRITE_ONLY;
+        createCacheFds(mModelCache, modelCacheMode, &modelCache);
+        createCacheFds(mDataCache, dataCacheMode, &dataCache);
+        dataCacheMode[i] = AccessMode::READ_WRITE;
+        verifyModelPreparationBehaviour(modelCache, dataCache, &model, testModel);
+    }
+}
+
+// Copy file contents between files.
+// The vector sizes must match.
+static void copyCacheFiles(const std::vector<std::string>& from,
+                           const std::vector<std::string>& to) {
+    constexpr size_t kBufferSize = 1000000;
+    uint8_t buffer[kBufferSize];
+
+    ASSERT_EQ(from.size(), to.size());
+    for (uint32_t i = 0; i < from.size(); i++) {
+        int fromFd = open(from[i].c_str(), O_RDONLY);
+        int toFd = open(to[i].c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+        ASSERT_GE(fromFd, 0);
+        ASSERT_GE(toFd, 0);
+
+        ssize_t readBytes;
+        while ((readBytes = read(fromFd, &buffer, kBufferSize)) > 0) {
+            ASSERT_EQ(write(toFd, &buffer, readBytes), readBytes);
+        }
+        ASSERT_GE(readBytes, 0);
+
+        close(fromFd);
+        close(toFd);
+    }
+}
+
+// Number of operations in the large test model.
+constexpr uint32_t kLargeModelSize = 100;
+constexpr uint32_t kNumIterationsTOCTOU = 100;
+
+TEST_P(CompilationCachingTest, SaveToCache_TOCTOU) {
+    if (!mIsCachingSupported) return;
+
+    // Create test models and check if fully supported by the service.
+    const TestModel testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    const Model modelMul = createModel(testModelMul);
+    if (checkEarlyTermination(modelMul)) return;
+    const TestModel testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    const Model modelAdd = createModel(testModelAdd);
+    if (checkEarlyTermination(modelAdd)) return;
+
+    // Save the modelMul compilation to cache.
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache.append("_mul");
+    }
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(modelMul, modelCache, dataCache);
+    }
+
+    // Use a different token for modelAdd.
+    mToken[0]++;
+
+    // This test is probabilistic, so we run it multiple times.
+    for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+        // Save the modelAdd compilation to cache.
+        {
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+
+            // Spawn a thread to copy the cache content concurrently while saving to cache.
+            std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
+            saveModelToCache(modelAdd, modelCache, dataCache);
+            thread.join();
+        }
+
+        // Retrieve preparedModel from cache.
+        {
+            std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+            ErrorStatus status;
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                                  /*fallbackModel=*/nullptr);
+
+            // The preparation may fail or succeed, but must not crash. If the preparation succeeds,
+            // the prepared model must be executed with the correct result and not crash.
+            if (status != ErrorStatus::NONE) {
+                ASSERT_EQ(preparedModel, nullptr);
+            } else {
+                ASSERT_NE(preparedModel, nullptr);
+                EvaluatePreparedModel(kDevice, preparedModel, testModelAdd,
+                                      /*testKind=*/TestKind::GENERAL);
+            }
+        }
+    }
+}
+
+TEST_P(CompilationCachingTest, PrepareFromCache_TOCTOU) {
+    if (!mIsCachingSupported) return;
+
+    // Create test models and check if fully supported by the service.
+    const TestModel testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    const Model modelMul = createModel(testModelMul);
+    if (checkEarlyTermination(modelMul)) return;
+    const TestModel testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    const Model modelAdd = createModel(testModelAdd);
+    if (checkEarlyTermination(modelAdd)) return;
+
+    // Save the modelMul compilation to cache.
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache.append("_mul");
+    }
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(modelMul, modelCache, dataCache);
+    }
+
+    // Use a different token for modelAdd.
+    mToken[0]++;
+
+    // This test is probabilistic, so we run it multiple times.
+    for (uint32_t i = 0; i < kNumIterationsTOCTOU; i++) {
+        // Save the modelAdd compilation to cache.
+        {
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            saveModelToCache(modelAdd, modelCache, dataCache);
+        }
+
+        // Retrieve preparedModel from cache.
+        {
+            std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+            ErrorStatus status;
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+
+            // Spawn a thread to copy the cache content concurrently while preparing from cache.
+            std::thread thread(copyCacheFiles, std::cref(modelCacheMul), std::cref(mModelCache));
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status,
+                                  /*fallbackModel=*/nullptr);
+            thread.join();
+
+            // The preparation may fail or succeed, but must not crash. If the preparation succeeds,
+            // the prepared model must be executed with the correct result and not crash.
+            if (status != ErrorStatus::NONE) {
+                ASSERT_EQ(preparedModel, nullptr);
+            } else {
+                ASSERT_NE(preparedModel, nullptr);
+                EvaluatePreparedModel(kDevice, preparedModel, testModelAdd,
+                                      /*testKind=*/TestKind::GENERAL);
+            }
+        }
+    }
+}
+
+TEST_P(CompilationCachingTest, ReplaceSecuritySensitiveCache) {
+    if (!mIsCachingSupported) return;
+
+    // Create test models and check if fully supported by the service.
+    const TestModel testModelMul = createLargeTestModel(OperationType::MUL, kLargeModelSize);
+    const Model modelMul = createModel(testModelMul);
+    if (checkEarlyTermination(modelMul)) return;
+    const TestModel testModelAdd = createLargeTestModel(OperationType::ADD, kLargeModelSize);
+    const Model modelAdd = createModel(testModelAdd);
+    if (checkEarlyTermination(modelAdd)) return;
+
+    // Save the modelMul compilation to cache.
+    auto modelCacheMul = mModelCache;
+    for (auto& cache : modelCacheMul) {
+        cache.append("_mul");
+    }
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(modelCacheMul, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(modelMul, modelCache, dataCache);
+    }
+
+    // Use a different token for modelAdd.
+    mToken[0]++;
+
+    // Save the modelAdd compilation to cache.
+    {
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        saveModelToCache(modelAdd, modelCache, dataCache);
+    }
+
+    // Replace the model cache of modelAdd with modelMul.
+    copyCacheFiles(modelCacheMul, mModelCache);
+
+    // Retrieve the preparedModel from cache, expect failure.
+    {
+        std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+        ErrorStatus status;
+        std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+        createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+        createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+        prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+        ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+        ASSERT_EQ(preparedModel, nullptr);
+    }
+}
+
+// TODO(b/179270601): restore kNamedDeviceChoices.
+static const auto kOperandTypeChoices =
+        testing::Values(OperandType::TENSOR_FLOAT32, OperandType::TENSOR_QUANT8_ASYMM);
+
+std::string printCompilationCachingTest(
+        const testing::TestParamInfo<CompilationCachingTestParam>& info) {
+    const auto& [namedDevice, operandType] = info.param;
+    const std::string type = (operandType == OperandType::TENSOR_FLOAT32 ? "float32" : "quant8");
+    return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingTest,
+                         testing::Combine(testing::ValuesIn(getNamedDevices()),
+                                          kOperandTypeChoices),
+                         printCompilationCachingTest);
+
+using CompilationCachingSecurityTestParam = std::tuple<NamedDevice, OperandType, uint32_t>;
+
+class CompilationCachingSecurityTest
+    : public CompilationCachingTestBase,
+      public testing::WithParamInterface<CompilationCachingSecurityTestParam> {
+  protected:
+    CompilationCachingSecurityTest()
+        : CompilationCachingTestBase(getData(std::get<NamedDevice>(GetParam())),
+                                     std::get<OperandType>(GetParam())) {}
+
+    void SetUp() {
+        CompilationCachingTestBase::SetUp();
+        generator.seed(kSeed);
+    }
+
+    // Get a random integer within a closed range [lower, upper].
+    template <typename T>
+    T getRandomInt(T lower, T upper) {
+        std::uniform_int_distribution<T> dis(lower, upper);
+        return dis(generator);
+    }
+
+    // Randomly flip one single bit of the cache entry.
+    void flipOneBitOfCache(const std::string& filename, bool* skip) {
+        FILE* pFile = fopen(filename.c_str(), "r+");
+        ASSERT_EQ(fseek(pFile, 0, SEEK_END), 0);
+        long int fileSize = ftell(pFile);
+        if (fileSize == 0) {
+            fclose(pFile);
+            *skip = true;
+            return;
+        }
+        ASSERT_EQ(fseek(pFile, getRandomInt(0l, fileSize - 1), SEEK_SET), 0);
+        int readByte = fgetc(pFile);
+        ASSERT_NE(readByte, EOF);
+        ASSERT_EQ(fseek(pFile, -1, SEEK_CUR), 0);
+        ASSERT_NE(fputc(static_cast<uint8_t>(readByte) ^ (1U << getRandomInt(0, 7)), pFile), EOF);
+        fclose(pFile);
+        *skip = false;
+    }
+
+    // Randomly append bytes to the cache entry.
+    void appendBytesToCache(const std::string& filename, bool* skip) {
+        FILE* pFile = fopen(filename.c_str(), "a");
+        uint32_t appendLength = getRandomInt(1, 256);
+        for (uint32_t i = 0; i < appendLength; i++) {
+            ASSERT_NE(fputc(getRandomInt<uint8_t>(0, 255), pFile), EOF);
+        }
+        fclose(pFile);
+        *skip = false;
+    }
+
+    enum class ExpectedResult { GENERAL_FAILURE, NOT_CRASH };
+
+    // Test if the driver behaves as expected when given corrupted cache or token.
+    // The modifier will be invoked after save to cache but before prepare from cache.
+    // The modifier accepts one pointer argument "skip" as the returning value, indicating
+    // whether the test should be skipped or not.
+    void testCorruptedCache(ExpectedResult expected, std::function<void(bool*)> modifier) {
+        const TestModel& testModel = createTestModel();
+        const Model model = createModel(testModel);
+        if (checkEarlyTermination(model)) return;
+
+        // Save the compilation to cache.
+        {
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            saveModelToCache(model, modelCache, dataCache);
+        }
+
+        bool skip = false;
+        modifier(&skip);
+        if (skip) return;
+
+        // Retrieve preparedModel from cache.
+        {
+            std::shared_ptr<IPreparedModel> preparedModel = nullptr;
+            ErrorStatus status;
+            std::vector<ndk::ScopedFileDescriptor> modelCache, dataCache;
+            createCacheFds(mModelCache, AccessMode::READ_WRITE, &modelCache);
+            createCacheFds(mDataCache, AccessMode::READ_WRITE, &dataCache);
+            prepareModelFromCache(modelCache, dataCache, &preparedModel, &status);
+
+            switch (expected) {
+                case ExpectedResult::GENERAL_FAILURE:
+                    ASSERT_EQ(status, ErrorStatus::GENERAL_FAILURE);
+                    ASSERT_EQ(preparedModel, nullptr);
+                    break;
+                case ExpectedResult::NOT_CRASH:
+                    ASSERT_EQ(preparedModel == nullptr, status != ErrorStatus::NONE);
+                    break;
+                default:
+                    FAIL();
+            }
+        }
+    }
+
+    const uint32_t kSeed = std::get<uint32_t>(GetParam());
+    std::mt19937 generator;
+};
+
+TEST_P(CompilationCachingSecurityTest, CorruptedModelCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        testCorruptedCache(ExpectedResult::GENERAL_FAILURE,
+                           [this, i](bool* skip) { flipOneBitOfCache(mModelCache[i], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthModelCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumModelCache; i++) {
+        testCorruptedCache(ExpectedResult::GENERAL_FAILURE,
+                           [this, i](bool* skip) { appendBytesToCache(mModelCache[i], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, CorruptedDataCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        testCorruptedCache(ExpectedResult::NOT_CRASH,
+                           [this, i](bool* skip) { flipOneBitOfCache(mDataCache[i], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongLengthDataCache) {
+    if (!mIsCachingSupported) return;
+    for (uint32_t i = 0; i < mNumDataCache; i++) {
+        testCorruptedCache(ExpectedResult::NOT_CRASH,
+                           [this, i](bool* skip) { appendBytesToCache(mDataCache[i], skip); });
+    }
+}
+
+TEST_P(CompilationCachingSecurityTest, WrongToken) {
+    if (!mIsCachingSupported) return;
+    testCorruptedCache(ExpectedResult::GENERAL_FAILURE, [this](bool* skip) {
+        // Randomly flip one single bit in mToken.
+        uint32_t ind =
+                getRandomInt(0u, static_cast<uint32_t>(IDevice::BYTE_SIZE_OF_CACHE_TOKEN) - 1);
+        mToken[ind] ^= (1U << getRandomInt(0, 7));
+        *skip = false;
+    });
+}
+
+std::string printCompilationCachingSecurityTest(
+        const testing::TestParamInfo<CompilationCachingSecurityTestParam>& info) {
+    const auto& [namedDevice, operandType, seed] = info.param;
+    const std::string type = (operandType == OperandType::TENSOR_FLOAT32 ? "float32" : "quant8");
+    return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + std::to_string(seed));
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(CompilationCachingSecurityTest);
+INSTANTIATE_TEST_SUITE_P(TestCompilationCaching, CompilationCachingSecurityTest,
+                         testing::Combine(testing::ValuesIn(getNamedDevices()), kOperandTypeChoices,
+                                          testing::Range(0U, 10U)),
+                         printCompilationCachingSecurityTest);
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
new file mode 100644
index 0000000..2356ff0
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.cpp
@@ -0,0 +1,986 @@
+/*
+ * 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.
+ */
+
+#include "GeneratedTestHarness.h"
+
+#include <aidl/android/hardware/neuralnetworks/ErrorStatus.h>
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/sync.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <chrono>
+#include <iostream>
+#include <iterator>
+#include <numeric>
+#include <vector>
+
+#include <MemoryUtils.h>
+#include <android/binder_status.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/aidl/Conversions.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+#include "Callbacks.h"
+#include "TestHarness.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+namespace nn = ::android::nn;
+using namespace test_helper;
+using implementation::PreparedModelCallback;
+
+namespace {
+
+enum class OutputType { FULLY_SPECIFIED, UNSPECIFIED, INSUFFICIENT, MISSED_DEADLINE };
+
+struct TestConfig {
+    Executor executor;
+    bool measureTiming;
+    OutputType outputType;
+    MemoryType memoryType;
+    // `reportSkipping` indicates if a test should print an info message in case
+    // it is skipped. The field is set to true by default and is set to false in
+    // quantization coupling tests to suppress skipping a test
+    bool reportSkipping;
+    TestConfig(Executor executor, bool measureTiming, OutputType outputType, MemoryType memoryType)
+        : executor(executor),
+          measureTiming(measureTiming),
+          outputType(outputType),
+          memoryType(memoryType),
+          reportSkipping(true) {}
+    TestConfig(Executor executor, bool measureTiming, OutputType outputType, MemoryType memoryType,
+               bool reportSkipping)
+        : executor(executor),
+          measureTiming(measureTiming),
+          outputType(outputType),
+          memoryType(memoryType),
+          reportSkipping(reportSkipping) {}
+};
+
+enum class IOType { INPUT, OUTPUT };
+
+class DeviceMemoryAllocator {
+  public:
+    DeviceMemoryAllocator(const std::shared_ptr<IDevice>& device,
+                          const std::shared_ptr<IPreparedModel>& preparedModel,
+                          const TestModel& testModel)
+        : kDevice(device), kPreparedModel(preparedModel), kTestModel(testModel) {}
+
+    // Allocate device memory for a target input/output operand.
+    // Return {IBuffer object, token} if successful.
+    // Return {nullptr, 0} if device memory is not supported.
+    template <IOType ioType>
+    std::pair<std::shared_ptr<IBuffer>, int32_t> allocate(uint32_t index) {
+        std::pair<std::shared_ptr<IBuffer>, int32_t> buffer;
+        allocateInternal<ioType>(index, &buffer);
+        return buffer;
+    }
+
+  private:
+    template <IOType ioType>
+    void allocateInternal(int32_t index, std::pair<std::shared_ptr<IBuffer>, int32_t>* result) {
+        ASSERT_NE(result, nullptr);
+
+        // Prepare arguments.
+        BufferRole role = {.modelIndex = 0, .ioIndex = index, .probability = 1.0f};
+        std::vector<BufferRole> inputRoles, outputRoles;
+        if constexpr (ioType == IOType::INPUT) {
+            inputRoles = {role};
+        } else {
+            outputRoles = {role};
+        }
+
+        // Allocate device memory.
+        DeviceBuffer buffer;
+        IPreparedModelParcel parcel;
+        parcel.preparedModel = kPreparedModel;
+        const auto ret = kDevice->allocate({}, {parcel}, inputRoles, outputRoles, &buffer);
+
+        // Check allocation results.
+        if (ret.isOk()) {
+            ASSERT_NE(buffer.buffer, nullptr);
+            ASSERT_GT(buffer.token, 0);
+        } else {
+            ASSERT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            ASSERT_EQ(static_cast<ErrorStatus>(ret.getServiceSpecificError()),
+                      ErrorStatus::GENERAL_FAILURE);
+            buffer.buffer = nullptr;
+            buffer.token = 0;
+        }
+
+        // Initialize input data from TestBuffer.
+        if constexpr (ioType == IOType::INPUT) {
+            if (buffer.buffer != nullptr) {
+                // TestBuffer -> Shared memory.
+                const auto& testBuffer =
+                        kTestModel.main.operands[kTestModel.main.inputIndexes[index]].data;
+                ASSERT_GT(testBuffer.size(), 0);
+                const auto sharedMemory = nn::createSharedMemory(testBuffer.size()).value();
+                const auto memory = utils::convert(sharedMemory).value();
+                const auto mapping = nn::map(sharedMemory).value();
+                uint8_t* inputPtr = static_cast<uint8_t*>(std::get<void*>(mapping.pointer));
+                ASSERT_NE(inputPtr, nullptr);
+                const uint8_t* begin = testBuffer.get<uint8_t>();
+                const uint8_t* end = begin + testBuffer.size();
+                std::copy(begin, end, inputPtr);
+
+                // Shared memory -> IBuffer.
+                auto ret = buffer.buffer->copyFrom(memory, {});
+                ASSERT_TRUE(ret.isOk());
+            }
+        }
+        *result = {std::move(buffer.buffer), buffer.token};
+    }
+
+    const std::shared_ptr<IDevice> kDevice;
+    const std::shared_ptr<IPreparedModel> kPreparedModel;
+    const TestModel& kTestModel;
+};
+
+Subgraph createSubgraph(const TestSubgraph& testSubgraph, uint32_t* constCopySize,
+                        std::vector<const TestBuffer*>* constCopies, uint32_t* constRefSize,
+                        std::vector<const TestBuffer*>* constReferences) {
+    CHECK(constCopySize != nullptr);
+    CHECK(constCopies != nullptr);
+    CHECK(constRefSize != nullptr);
+    CHECK(constReferences != nullptr);
+
+    // Operands.
+    std::vector<Operand> operands(testSubgraph.operands.size());
+    for (uint32_t i = 0; i < testSubgraph.operands.size(); i++) {
+        const auto& op = testSubgraph.operands[i];
+
+        DataLocation loc = {};
+        if (op.lifetime == TestOperandLifeTime::CONSTANT_COPY) {
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *constCopySize,
+                    .length = static_cast<int64_t>(op.data.size()),
+            };
+            constCopies->push_back(&op.data);
+            *constCopySize += op.data.alignedSize();
+        } else if (op.lifetime == TestOperandLifeTime::CONSTANT_REFERENCE) {
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *constRefSize,
+                    .length = static_cast<int64_t>(op.data.size()),
+            };
+            constReferences->push_back(&op.data);
+            *constRefSize += op.data.alignedSize();
+        } else if (op.lifetime == TestOperandLifeTime::SUBGRAPH) {
+            loc = {
+                    .poolIndex = 0,
+                    .offset = *op.data.get<uint32_t>(),
+                    .length = 0,
+            };
+        }
+
+        std::optional<OperandExtraParams> extraParams;
+        if (op.type == TestOperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL) {
+            using Tag = OperandExtraParams::Tag;
+            extraParams = OperandExtraParams::make<Tag::channelQuant>(SymmPerChannelQuantParams{
+                    .scales = op.channelQuant.scales,
+                    .channelDim = static_cast<int32_t>(op.channelQuant.channelDim)});
+        }
+
+        operands[i] = {.type = static_cast<OperandType>(op.type),
+                       .dimensions = utils::toSigned(op.dimensions).value(),
+                       .scale = op.scale,
+                       .zeroPoint = op.zeroPoint,
+                       .lifetime = static_cast<OperandLifeTime>(op.lifetime),
+                       .location = loc,
+                       .extraParams = std::move(extraParams)};
+    }
+
+    // Operations.
+    std::vector<Operation> operations(testSubgraph.operations.size());
+    std::transform(testSubgraph.operations.begin(), testSubgraph.operations.end(),
+                   operations.begin(), [](const TestOperation& op) -> Operation {
+                       return {.type = static_cast<OperationType>(op.type),
+                               .inputs = utils::toSigned(op.inputs).value(),
+                               .outputs = utils::toSigned(op.outputs).value()};
+                   });
+
+    return {.operands = std::move(operands),
+            .operations = std::move(operations),
+            .inputIndexes = utils::toSigned(testSubgraph.inputIndexes).value(),
+            .outputIndexes = utils::toSigned(testSubgraph.outputIndexes).value()};
+}
+
+void copyTestBuffers(const std::vector<const TestBuffer*>& buffers, uint8_t* output) {
+    uint32_t offset = 0;
+    for (const TestBuffer* buffer : buffers) {
+        const uint8_t* begin = buffer->get<uint8_t>();
+        const uint8_t* end = begin + buffer->size();
+        std::copy(begin, end, output + offset);
+        offset += buffer->alignedSize();
+    }
+}
+
+}  // namespace
+
+void waitForSyncFence(int syncFd) {
+    constexpr int kInfiniteTimeout = -1;
+    ASSERT_GT(syncFd, 0);
+    int r = sync_wait(syncFd, kInfiniteTimeout);
+    ASSERT_GE(r, 0);
+}
+
+Model createModel(const TestModel& testModel) {
+    uint32_t constCopySize = 0;
+    uint32_t constRefSize = 0;
+    std::vector<const TestBuffer*> constCopies;
+    std::vector<const TestBuffer*> constReferences;
+
+    Subgraph mainSubgraph = createSubgraph(testModel.main, &constCopySize, &constCopies,
+                                           &constRefSize, &constReferences);
+    std::vector<Subgraph> refSubgraphs(testModel.referenced.size());
+    std::transform(testModel.referenced.begin(), testModel.referenced.end(), refSubgraphs.begin(),
+                   [&constCopySize, &constCopies, &constRefSize,
+                    &constReferences](const TestSubgraph& testSubgraph) {
+                       return createSubgraph(testSubgraph, &constCopySize, &constCopies,
+                                             &constRefSize, &constReferences);
+                   });
+
+    // Constant copies.
+    std::vector<uint8_t> operandValues(constCopySize);
+    copyTestBuffers(constCopies, operandValues.data());
+
+    // Shared memory.
+    std::vector<nn::SharedMemory> pools = {};
+    if (constRefSize > 0) {
+        const auto pool = nn::createSharedMemory(constRefSize).value();
+        pools.push_back(pool);
+
+        // load data
+        const auto mappedMemory = nn::map(pool).value();
+        uint8_t* mappedPtr = static_cast<uint8_t*>(std::get<void*>(mappedMemory.pointer));
+        CHECK(mappedPtr != nullptr);
+
+        copyTestBuffers(constReferences, mappedPtr);
+    }
+
+    std::vector<Memory> aidlPools;
+    aidlPools.reserve(pools.size());
+    for (auto& pool : pools) {
+        auto aidlPool = utils::convert(pool).value();
+        aidlPools.push_back(std::move(aidlPool));
+    }
+
+    return {.main = std::move(mainSubgraph),
+            .referenced = std::move(refSubgraphs),
+            .operandValues = std::move(operandValues),
+            .pools = std::move(aidlPools),
+            .relaxComputationFloat32toFloat16 = testModel.isRelaxed};
+}
+
+static bool isOutputSizeGreaterThanOne(const TestModel& testModel, uint32_t index) {
+    const auto byteSize = testModel.main.operands[testModel.main.outputIndexes[index]].data.size();
+    return byteSize > 1u;
+}
+
+static void makeOutputInsufficientSize(uint32_t outputIndex, Request* request) {
+    auto& loc = request->outputs[outputIndex].location;
+    ASSERT_GT(loc.length, 1u);
+    loc.length -= 1u;
+    // Test that the padding is not used for output data.
+    loc.padding += 1u;
+}
+
+static void makeOutputDimensionsUnspecified(Model* model) {
+    for (auto i : model->main.outputIndexes) {
+        auto& dims = model->main.operands[i].dimensions;
+        std::fill(dims.begin(), dims.end(), 0);
+    }
+}
+
+// Manages the lifetime of memory resources used in an execution.
+class ExecutionContext {
+  public:
+    ExecutionContext(std::shared_ptr<IDevice> device, std::shared_ptr<IPreparedModel> preparedModel)
+        : kDevice(std::move(device)), kPreparedModel(std::move(preparedModel)) {}
+
+    std::optional<Request> createRequest(const TestModel& testModel, MemoryType memoryType);
+    std::vector<TestBuffer> getOutputBuffers(const TestModel& testModel,
+                                             const Request& request) const;
+
+  private:
+    // Get a TestBuffer with data copied from an IBuffer object.
+    void getBuffer(const std::shared_ptr<IBuffer>& buffer, size_t size,
+                   TestBuffer* testBuffer) const;
+
+    static constexpr uint32_t kInputPoolIndex = 0;
+    static constexpr uint32_t kOutputPoolIndex = 1;
+    static constexpr uint32_t kDeviceMemoryBeginIndex = 2;
+
+    const std::shared_ptr<IDevice> kDevice;
+    const std::shared_ptr<IPreparedModel> kPreparedModel;
+    std::unique_ptr<TestMemoryBase> mInputMemory, mOutputMemory;
+    std::vector<std::shared_ptr<IBuffer>> mBuffers;
+};
+
+// Returns the number of bytes needed to round up "size" to the nearest multiple of "multiple".
+static uint32_t roundUpBytesNeeded(uint32_t size, uint32_t multiple) {
+    CHECK(multiple != 0);
+    return ((size + multiple - 1) / multiple) * multiple - size;
+}
+
+std::optional<Request> ExecutionContext::createRequest(const TestModel& testModel,
+                                                       MemoryType memoryType) {
+    // Memory pools are organized as:
+    // - 0: Input shared memory pool
+    // - 1: Output shared memory pool
+    // - [2, 2+i): Input device memories
+    // - [2+i, 2+i+o): Output device memories
+    DeviceMemoryAllocator allocator(kDevice, kPreparedModel, testModel);
+    std::vector<int32_t> tokens;
+    mBuffers.clear();
+
+    // Model inputs.
+    std::vector<RequestArgument> inputs(testModel.main.inputIndexes.size());
+    size_t inputSize = 0;
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
+        if (op.data.size() == 0) {
+            // Omitted input.
+            inputs[i] = {.hasNoValue = true};
+            continue;
+        } else if (memoryType == MemoryType::DEVICE) {
+            SCOPED_TRACE("Input index = " + std::to_string(i));
+            auto [buffer, token] = allocator.allocate<IOType::INPUT>(i);
+            if (buffer != nullptr) {
+                DataLocation loc = {.poolIndex = static_cast<int32_t>(mBuffers.size() +
+                                                                      kDeviceMemoryBeginIndex)};
+                mBuffers.push_back(std::move(buffer));
+                tokens.push_back(token);
+                inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+                continue;
+            }
+        }
+
+        // Reserve shared memory for input.
+        inputSize += roundUpBytesNeeded(inputSize, nn::kDefaultRequestMemoryAlignment);
+        const auto padding = roundUpBytesNeeded(op.data.size(), nn::kDefaultRequestMemoryPadding);
+        DataLocation loc = {.poolIndex = kInputPoolIndex,
+                            .offset = static_cast<int64_t>(inputSize),
+                            .length = static_cast<int64_t>(op.data.size()),
+                            .padding = static_cast<int64_t>(padding)};
+        inputSize += (op.data.size() + padding);
+        inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+    }
+
+    // Model outputs.
+    std::vector<RequestArgument> outputs(testModel.main.outputIndexes.size());
+    size_t outputSize = 0;
+    for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
+        if (memoryType == MemoryType::DEVICE) {
+            SCOPED_TRACE("Output index = " + std::to_string(i));
+            auto [buffer, token] = allocator.allocate<IOType::OUTPUT>(i);
+            if (buffer != nullptr) {
+                DataLocation loc = {.poolIndex = static_cast<int32_t>(mBuffers.size() +
+                                                                      kDeviceMemoryBeginIndex)};
+                mBuffers.push_back(std::move(buffer));
+                tokens.push_back(token);
+                outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+                continue;
+            }
+        }
+
+        // In the case of zero-sized output, we should at least provide a one-byte buffer.
+        // This is because zero-sized tensors are only supported internally to the driver, or
+        // reported in output shapes. It is illegal for the client to pre-specify a zero-sized
+        // tensor as model output. Otherwise, we will have two semantic conflicts:
+        // - "Zero dimension" conflicts with "unspecified dimension".
+        // - "Omitted operand buffer" conflicts with "zero-sized operand buffer".
+        size_t bufferSize = std::max<size_t>(op.data.size(), 1);
+
+        // Reserve shared memory for output.
+        outputSize += roundUpBytesNeeded(outputSize, nn::kDefaultRequestMemoryAlignment);
+        const auto padding = roundUpBytesNeeded(bufferSize, nn::kDefaultRequestMemoryPadding);
+        DataLocation loc = {.poolIndex = kOutputPoolIndex,
+                            .offset = static_cast<int64_t>(outputSize),
+                            .length = static_cast<int64_t>(bufferSize),
+                            .padding = static_cast<int64_t>(padding)};
+        outputSize += (bufferSize + padding);
+        outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+    }
+
+    if (memoryType == MemoryType::DEVICE && mBuffers.empty()) {
+        return std::nullopt;
+    }
+
+    // Memory pools.
+    if (memoryType == MemoryType::BLOB_AHWB) {
+        mInputMemory = TestBlobAHWB::create(std::max<size_t>(inputSize, 1));
+        mOutputMemory = TestBlobAHWB::create(std::max<size_t>(outputSize, 1));
+    } else {
+        mInputMemory = TestAshmem::create(std::max<size_t>(inputSize, 1), /*aidlReadonly=*/true);
+        mOutputMemory = TestAshmem::create(std::max<size_t>(outputSize, 1), /*aidlReadonly=*/false);
+    }
+    CHECK_NE(mInputMemory, nullptr);
+    CHECK_NE(mOutputMemory, nullptr);
+    std::vector<RequestMemoryPool> pools;
+    pools.reserve(kDeviceMemoryBeginIndex + mBuffers.size());
+
+    auto copiedInputMemory = utils::clone(*mInputMemory->getAidlMemory());
+    CHECK(copiedInputMemory.has_value()) << copiedInputMemory.error().message;
+    auto copiedOutputMemory = utils::clone(*mOutputMemory->getAidlMemory());
+    CHECK(copiedOutputMemory.has_value()) << copiedOutputMemory.error().message;
+
+    pools.push_back(RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+            std::move(copiedInputMemory).value()));
+    pools.push_back(RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+            std::move(copiedOutputMemory).value()));
+    for (const auto& token : tokens) {
+        pools.push_back(RequestMemoryPool::make<RequestMemoryPool::Tag::token>(token));
+    }
+
+    // Copy input data to the input shared memory pool.
+    uint8_t* inputPtr = mInputMemory->getPointer();
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        if (!inputs[i].hasNoValue && inputs[i].location.poolIndex == kInputPoolIndex) {
+            const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
+            const uint8_t* begin = op.data.get<uint8_t>();
+            const uint8_t* end = begin + op.data.size();
+            std::copy(begin, end, inputPtr + inputs[i].location.offset);
+        }
+    }
+    return Request{
+            .inputs = std::move(inputs), .outputs = std::move(outputs), .pools = std::move(pools)};
+}
+
+std::vector<TestBuffer> ExecutionContext::getOutputBuffers(const TestModel& testModel,
+                                                           const Request& request) const {
+    // Copy out output results.
+    uint8_t* outputPtr = mOutputMemory->getPointer();
+    std::vector<TestBuffer> outputBuffers;
+    for (uint32_t i = 0; i < request.outputs.size(); i++) {
+        const auto& outputLoc = request.outputs[i].location;
+        if (outputLoc.poolIndex == kOutputPoolIndex) {
+            outputBuffers.emplace_back(outputLoc.length, outputPtr + outputLoc.offset);
+        } else {
+            const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
+            if (op.data.size() == 0) {
+                outputBuffers.emplace_back(0, nullptr);
+            } else {
+                SCOPED_TRACE("Output index = " + std::to_string(i));
+                const uint32_t bufferIndex = outputLoc.poolIndex - kDeviceMemoryBeginIndex;
+                TestBuffer buffer;
+                getBuffer(mBuffers[bufferIndex], op.data.size(), &buffer);
+                outputBuffers.push_back(std::move(buffer));
+            }
+        }
+    }
+    return outputBuffers;
+}
+
+// Get a TestBuffer with data copied from an IBuffer object.
+void ExecutionContext::getBuffer(const std::shared_ptr<IBuffer>& buffer, size_t size,
+                                 TestBuffer* testBuffer) const {
+    // IBuffer -> Shared memory.
+    auto sharedMemory = nn::createSharedMemory(size).value();
+    auto aidlMemory = utils::convert(sharedMemory).value();
+    const auto ret = buffer->copyTo(aidlMemory);
+    ASSERT_TRUE(ret.isOk());
+
+    // Shared memory -> TestBuffer.
+    const auto outputMemory = nn::map(sharedMemory).value();
+    const uint8_t* outputPtr = std::visit(
+            [](auto* ptr) { return static_cast<const uint8_t*>(ptr); }, outputMemory.pointer);
+    ASSERT_NE(outputPtr, nullptr);
+    ASSERT_NE(testBuffer, nullptr);
+    *testBuffer = TestBuffer(size, outputPtr);
+}
+
+static bool hasZeroSizedOutput(const TestModel& testModel) {
+    return std::any_of(testModel.main.outputIndexes.begin(), testModel.main.outputIndexes.end(),
+                       [&testModel](uint32_t index) {
+                           return testModel.main.operands[index].data.size() == 0;
+                       });
+}
+
+void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device,
+                           const std::shared_ptr<IPreparedModel>& preparedModel,
+                           const TestModel& testModel, const TestConfig& testConfig,
+                           bool* skipped = nullptr) {
+    if (skipped != nullptr) {
+        *skipped = false;
+    }
+    // If output0 does not have size larger than one byte, we can not test with insufficient buffer.
+    if (testConfig.outputType == OutputType::INSUFFICIENT &&
+        !isOutputSizeGreaterThanOne(testModel, 0)) {
+        return;
+    }
+
+    ExecutionContext context(device, preparedModel);
+    auto maybeRequest = context.createRequest(testModel, testConfig.memoryType);
+    // Skip if testing memory domain but no device memory has been allocated.
+    if (!maybeRequest.has_value()) {
+        return;
+    }
+
+    Request request = std::move(maybeRequest).value();
+
+    constexpr uint32_t kInsufficientOutputIndex = 0;
+    if (testConfig.outputType == OutputType::INSUFFICIENT) {
+        makeOutputInsufficientSize(kInsufficientOutputIndex, &request);
+    }
+
+    int64_t loopTimeoutDurationNs = kOmittedTimeoutDuration;
+    // OutputType::MISSED_DEADLINE is only used by
+    // TestKind::INTINITE_LOOP_TIMEOUT tests to verify that an infinite loop is
+    // aborted after a timeout.
+    if (testConfig.outputType == OutputType::MISSED_DEADLINE) {
+        // Override the default loop timeout duration with a small value to
+        // speed up test execution.
+        constexpr int64_t kMillisecond = 1'000'000;
+        loopTimeoutDurationNs = 1 * kMillisecond;
+    }
+
+    ErrorStatus executionStatus;
+    std::vector<OutputShape> outputShapes;
+    Timing timing = kNoTiming;
+    switch (testConfig.executor) {
+        case Executor::SYNC: {
+            SCOPED_TRACE("synchronous");
+
+            ExecutionResult executionResult;
+            // execute
+            const auto ret = preparedModel->executeSynchronously(request, testConfig.measureTiming,
+                                                                 kNoDeadline, loopTimeoutDurationNs,
+                                                                 &executionResult);
+            ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+                    << ret.getDescription();
+            if (ret.isOk()) {
+                executionStatus = executionResult.outputSufficientSize
+                                          ? ErrorStatus::NONE
+                                          : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+                outputShapes = std::move(executionResult.outputShapes);
+                timing = executionResult.timing;
+            } else {
+                executionStatus = static_cast<ErrorStatus>(ret.getServiceSpecificError());
+            }
+            break;
+        }
+        case Executor::BURST: {
+            SCOPED_TRACE("burst");
+
+            // create burst
+            std::shared_ptr<IBurst> burst;
+            auto ret = preparedModel->configureExecutionBurst(&burst);
+            ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+            ASSERT_NE(nullptr, burst.get());
+
+            // associate a unique slot with each memory pool
+            int64_t currentSlot = 0;
+            std::vector<int64_t> slots;
+            slots.reserve(request.pools.size());
+            for (const auto& pool : request.pools) {
+                if (pool.getTag() == RequestMemoryPool::Tag::pool) {
+                    slots.push_back(currentSlot++);
+                } else {
+                    EXPECT_EQ(pool.getTag(), RequestMemoryPool::Tag::token);
+                    slots.push_back(-1);
+                }
+            }
+
+            ExecutionResult executionResult;
+            // execute
+            ret = burst->executeSynchronously(request, slots, testConfig.measureTiming, kNoDeadline,
+                                              loopTimeoutDurationNs, &executionResult);
+            ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+                    << ret.getDescription();
+            if (ret.isOk()) {
+                executionStatus = executionResult.outputSufficientSize
+                                          ? ErrorStatus::NONE
+                                          : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+                outputShapes = std::move(executionResult.outputShapes);
+                timing = executionResult.timing;
+            } else {
+                executionStatus = static_cast<ErrorStatus>(ret.getServiceSpecificError());
+            }
+
+            // Mark each slot as unused after the execution. This is unnecessary because the burst
+            // is freed after this scope ends, but this is here to test the functionality.
+            for (int64_t slot : slots) {
+                ret = burst->releaseMemoryResource(slot);
+                ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+            }
+
+            break;
+        }
+        case Executor::FENCED: {
+            SCOPED_TRACE("fenced");
+            ErrorStatus result = ErrorStatus::NONE;
+            FencedExecutionResult executionResult;
+            auto ret = preparedModel->executeFenced(request, {}, testConfig.measureTiming,
+                                                    kNoDeadline, loopTimeoutDurationNs, kNoDuration,
+                                                    &executionResult);
+            ASSERT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+                    << ret.getDescription();
+            if (!ret.isOk()) {
+                result = static_cast<ErrorStatus>(ret.getServiceSpecificError());
+                executionStatus = result;
+            } else if (executionResult.syncFence.get() != -1) {
+                std::vector<ndk::ScopedFileDescriptor> waitFor;
+                auto dupFd = dup(executionResult.syncFence.get());
+                ASSERT_NE(dupFd, -1);
+                waitFor.emplace_back(dupFd);
+                // If a sync fence is returned, try start another run waiting for the sync fence.
+                ret = preparedModel->executeFenced(request, waitFor, testConfig.measureTiming,
+                                                   kNoDeadline, loopTimeoutDurationNs, kNoDuration,
+                                                   &executionResult);
+                ASSERT_TRUE(ret.isOk());
+                waitForSyncFence(executionResult.syncFence.get());
+            }
+            if (result == ErrorStatus::NONE) {
+                ASSERT_NE(executionResult.callback, nullptr);
+                Timing timingFenced;
+                auto ret = executionResult.callback->getExecutionInfo(&timing, &timingFenced,
+                                                                      &executionStatus);
+                ASSERT_TRUE(ret.isOk());
+            }
+            break;
+        }
+        default: {
+            FAIL() << "Unsupported execution mode for AIDL interface.";
+        }
+    }
+
+    if (testConfig.outputType != OutputType::FULLY_SPECIFIED &&
+        executionStatus == ErrorStatus::GENERAL_FAILURE) {
+        if (skipped != nullptr) {
+            *skipped = true;
+        }
+        if (!testConfig.reportSkipping) {
+            return;
+        }
+        LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                     "execute model that it does not support.";
+        std::cout << "[          ]   Early termination of test because vendor service cannot "
+                     "execute model that it does not support."
+                  << std::endl;
+        GTEST_SKIP();
+    }
+    if (!testConfig.measureTiming) {
+        EXPECT_EQ(timing, kNoTiming);
+    } else {
+        if (timing.timeOnDeviceNs != -1 && timing.timeInDriverNs != -1) {
+            EXPECT_LE(timing.timeOnDeviceNs, timing.timeInDriverNs);
+        }
+    }
+
+    switch (testConfig.outputType) {
+        case OutputType::FULLY_SPECIFIED:
+            if (testConfig.executor == Executor::FENCED && hasZeroSizedOutput(testModel)) {
+                // Executor::FENCED does not support zero-sized output.
+                ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionStatus);
+                return;
+            }
+            // If the model output operands are fully specified, outputShapes must be either
+            // either empty, or have the same number of elements as the number of outputs.
+            ASSERT_EQ(ErrorStatus::NONE, executionStatus);
+            ASSERT_TRUE(outputShapes.size() == 0 ||
+                        outputShapes.size() == testModel.main.outputIndexes.size());
+            break;
+        case OutputType::UNSPECIFIED:
+            if (testConfig.executor == Executor::FENCED) {
+                // For Executor::FENCED, the output shape must be fully specified.
+                ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionStatus);
+                return;
+            }
+            // If the model output operands are not fully specified, outputShapes must have
+            // the same number of elements as the number of outputs.
+            ASSERT_EQ(ErrorStatus::NONE, executionStatus);
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
+            break;
+        case OutputType::INSUFFICIENT:
+            if (testConfig.executor == Executor::FENCED) {
+                // For Executor::FENCED, the output shape must be fully specified.
+                ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, executionStatus);
+                return;
+            }
+            ASSERT_EQ(ErrorStatus::OUTPUT_INSUFFICIENT_SIZE, executionStatus);
+            ASSERT_EQ(outputShapes.size(), testModel.main.outputIndexes.size());
+            // Check that all returned output dimensions are at least as fully specified as the
+            // union of the information about the corresponding operand in the model and in the
+            // request. In this test, all model outputs have known rank with all dimensions
+            // unspecified, and no dimensional information is provided in the request.
+            for (uint32_t i = 0; i < outputShapes.size(); i++) {
+                ASSERT_EQ(outputShapes[i].isSufficient, i != kInsufficientOutputIndex);
+                const auto& actual = outputShapes[i].dimensions;
+                const auto& golden =
+                        testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
+                ASSERT_EQ(actual.size(), golden.size());
+                for (uint32_t j = 0; j < actual.size(); j++) {
+                    if (actual[j] == 0) continue;
+                    EXPECT_EQ(actual[j], golden[j]) << "index: " << j;
+                }
+            }
+            return;
+        case OutputType::MISSED_DEADLINE:
+            ASSERT_TRUE(executionStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+                        executionStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT)
+                    << "executionStatus = " << executionStatus;
+            return;
+    }
+
+    // Go through all outputs, check returned output shapes.
+    for (uint32_t i = 0; i < outputShapes.size(); i++) {
+        EXPECT_TRUE(outputShapes[i].isSufficient);
+        const auto& expect = testModel.main.operands[testModel.main.outputIndexes[i]].dimensions;
+        const auto unsignedActual = nn::toUnsigned(outputShapes[i].dimensions);
+        ASSERT_TRUE(unsignedActual.has_value());
+        const std::vector<uint32_t>& actual = unsignedActual.value();
+        EXPECT_EQ(expect, actual);
+    }
+
+    // Retrieve execution results.
+    const std::vector<TestBuffer> outputs = context.getOutputBuffers(testModel, request);
+
+    // We want "close-enough" results.
+    checkResults(testModel, outputs);
+}
+
+void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device,
+                           const std::shared_ptr<IPreparedModel>& preparedModel,
+                           const TestModel& testModel, TestKind testKind) {
+    std::vector<OutputType> outputTypesList;
+    std::vector<bool> measureTimingList;
+    std::vector<Executor> executorList;
+    std::vector<MemoryType> memoryTypeList;
+
+    switch (testKind) {
+        case TestKind::GENERAL: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {false, true};
+            executorList = {Executor::SYNC, Executor::BURST};
+            memoryTypeList = {MemoryType::ASHMEM};
+        } break;
+        case TestKind::DYNAMIC_SHAPE: {
+            outputTypesList = {OutputType::UNSPECIFIED, OutputType::INSUFFICIENT};
+            measureTimingList = {false, true};
+            executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+            memoryTypeList = {MemoryType::ASHMEM};
+        } break;
+        case TestKind::MEMORY_DOMAIN: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {false};
+            executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+            memoryTypeList = {MemoryType::BLOB_AHWB, MemoryType::DEVICE};
+        } break;
+        case TestKind::FENCED_COMPUTE: {
+            outputTypesList = {OutputType::FULLY_SPECIFIED};
+            measureTimingList = {false, true};
+            executorList = {Executor::FENCED};
+            memoryTypeList = {MemoryType::ASHMEM};
+        } break;
+        case TestKind::QUANTIZATION_COUPLING: {
+            LOG(FATAL) << "Wrong TestKind for EvaluatePreparedModel";
+            return;
+        } break;
+        case TestKind::INTINITE_LOOP_TIMEOUT: {
+            outputTypesList = {OutputType::MISSED_DEADLINE};
+            measureTimingList = {false, true};
+            executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+            memoryTypeList = {MemoryType::ASHMEM};
+        } break;
+    }
+
+    for (const OutputType outputType : outputTypesList) {
+        for (const bool measureTiming : measureTimingList) {
+            for (const Executor executor : executorList) {
+                for (const MemoryType memoryType : memoryTypeList) {
+                    const TestConfig testConfig(executor, measureTiming, outputType, memoryType);
+                    EvaluatePreparedModel(device, preparedModel, testModel, testConfig);
+                }
+            }
+        }
+    }
+}
+
+void EvaluatePreparedCoupledModels(const std::shared_ptr<IDevice>& device,
+                                   const std::shared_ptr<IPreparedModel>& preparedModel,
+                                   const TestModel& testModel,
+                                   const std::shared_ptr<IPreparedModel>& preparedCoupledModel,
+                                   const TestModel& coupledModel) {
+    const std::vector<OutputType> outputTypesList = {OutputType::FULLY_SPECIFIED};
+    const std::vector<bool> measureTimingList = {false, true};
+    const std::vector<Executor> executorList = {Executor::SYNC, Executor::BURST, Executor::FENCED};
+
+    for (const OutputType outputType : outputTypesList) {
+        for (const bool measureTiming : measureTimingList) {
+            for (const Executor executor : executorList) {
+                const TestConfig testConfig(executor, measureTiming, outputType, MemoryType::ASHMEM,
+                                            /*reportSkipping=*/false);
+                bool baseSkipped = false;
+                EvaluatePreparedModel(device, preparedModel, testModel, testConfig, &baseSkipped);
+                bool coupledSkipped = false;
+                EvaluatePreparedModel(device, preparedCoupledModel, coupledModel, testConfig,
+                                      &coupledSkipped);
+                ASSERT_EQ(baseSkipped, coupledSkipped);
+                if (baseSkipped) {
+                    LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                                 "execute model that it does not support.";
+                    std::cout << "[          ]   Early termination of test because vendor service "
+                                 "cannot "
+                                 "execute model that it does not support."
+                              << std::endl;
+                    GTEST_SKIP();
+                }
+            }
+        }
+    }
+}
+
+void Execute(const std::shared_ptr<IDevice>& device, const TestModel& testModel,
+             TestKind testKind) {
+    Model model = createModel(testModel);
+    if (testKind == TestKind::DYNAMIC_SHAPE) {
+        makeOutputDimensionsUnspecified(&model);
+    }
+
+    std::shared_ptr<IPreparedModel> preparedModel;
+    switch (testKind) {
+        case TestKind::GENERAL:
+        case TestKind::DYNAMIC_SHAPE:
+        case TestKind::MEMORY_DOMAIN:
+        case TestKind::FENCED_COMPUTE:
+        case TestKind::INTINITE_LOOP_TIMEOUT: {
+            createPreparedModel(device, model, &preparedModel);
+            if (preparedModel == nullptr) return;
+            EvaluatePreparedModel(device, preparedModel, testModel, testKind);
+        } break;
+        case TestKind::QUANTIZATION_COUPLING: {
+            ASSERT_TRUE(testModel.hasQuant8CoupledOperands());
+            createPreparedModel(device, model, &preparedModel,
+                                /*reportSkipping*/ false);
+            TestModel signedQuantizedModel = convertQuant8AsymmOperandsToSigned(testModel);
+            std::shared_ptr<IPreparedModel> preparedCoupledModel;
+            createPreparedModel(device, createModel(signedQuantizedModel), &preparedCoupledModel,
+                                /*reportSkipping*/ false);
+            // If we couldn't prepare a model with unsigned quantization, we must
+            // fail to prepare a model with signed quantization as well.
+            if (preparedModel == nullptr) {
+                ASSERT_EQ(preparedCoupledModel, nullptr);
+                // If we failed to prepare both of the models, we can safely skip
+                // the test.
+                LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot "
+                             "prepare model that it does not support.";
+                std::cout
+                        << "[          ]   Early termination of test because vendor service cannot "
+                           "prepare model that it does not support."
+                        << std::endl;
+                GTEST_SKIP();
+            }
+            ASSERT_NE(preparedCoupledModel, nullptr);
+            EvaluatePreparedCoupledModels(device, preparedModel, testModel, preparedCoupledModel,
+                                          signedQuantizedModel);
+        } break;
+    }
+}
+
+void GeneratedTestBase::SetUp() {
+    testing::TestWithParam<GeneratedTestParam>::SetUp();
+    ASSERT_NE(kDevice, nullptr);
+}
+
+std::vector<NamedModel> getNamedModels(const FilterFn& filter) {
+    return TestModelManager::get().getTestModels(filter);
+}
+
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter) {
+    return TestModelManager::get().getTestModels(filter);
+}
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info) {
+    const auto& [namedDevice, namedModel] = info.param;
+    return gtestCompliantName(getName(namedDevice) + "_" + getName(namedModel));
+}
+
+// Tag for the generated tests
+class GeneratedTest : public GeneratedTestBase {};
+
+// Tag for the dynamic output shape tests
+class DynamicOutputShapeTest : public GeneratedTest {};
+
+// Tag for the memory domain tests
+class MemoryDomainTest : public GeneratedTest {};
+
+// Tag for the fenced compute tests
+class FencedComputeTest : public GeneratedTest {};
+
+// Tag for the dynamic output shape tests
+class QuantizationCouplingTest : public GeneratedTest {};
+
+// Tag for the loop timeout tests
+class InfiniteLoopTimeoutTest : public GeneratedTest {};
+
+TEST_P(GeneratedTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::GENERAL);
+}
+
+TEST_P(DynamicOutputShapeTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::DYNAMIC_SHAPE);
+}
+
+TEST_P(MemoryDomainTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::MEMORY_DOMAIN);
+}
+
+TEST_P(FencedComputeTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::FENCED_COMPUTE);
+}
+
+TEST_P(QuantizationCouplingTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::QUANTIZATION_COUPLING);
+}
+
+TEST_P(InfiniteLoopTimeoutTest, Test) {
+    Execute(kDevice, kTestModel, TestKind::INTINITE_LOOP_TIMEOUT);
+}
+
+INSTANTIATE_GENERATED_TEST(GeneratedTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+INSTANTIATE_GENERATED_TEST(DynamicOutputShapeTest, [](const TestModel& testModel) {
+    return !testModel.expectFailure && !testModel.hasScalarOutputs();
+});
+
+INSTANTIATE_GENERATED_TEST(MemoryDomainTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+INSTANTIATE_GENERATED_TEST(FencedComputeTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+INSTANTIATE_GENERATED_TEST(QuantizationCouplingTest, [](const TestModel& testModel) {
+    return !testModel.expectFailure && testModel.hasQuant8CoupledOperands() &&
+           testModel.main.operations.size() == 1;
+});
+
+INSTANTIATE_GENERATED_TEST(InfiniteLoopTimeoutTest, [](const TestModel& testModel) {
+    return testModel.isInfiniteLoopTimeoutTest();
+});
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
new file mode 100644
index 0000000..ad40f06
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/GeneratedTestHarness.h
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_AIDL_GENERATED_TEST_HARNESS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_GENERATED_TEST_HARNESS_H
+
+#include <functional>
+#include <vector>
+
+#include <TestHarness.h>
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using NamedModel = Named<const test_helper::TestModel*>;
+using GeneratedTestParam = std::tuple<NamedDevice, NamedModel>;
+
+class GeneratedTestBase : public testing::TestWithParam<GeneratedTestParam> {
+  protected:
+    void SetUp() override;
+    const std::shared_ptr<IDevice> kDevice = getData(std::get<NamedDevice>(GetParam()));
+    const test_helper::TestModel& kTestModel = *getData(std::get<NamedModel>(GetParam()));
+};
+
+using FilterFn = std::function<bool(const test_helper::TestModel&)>;
+std::vector<NamedModel> getNamedModels(const FilterFn& filter);
+
+using FilterNameFn = std::function<bool(const std::string&)>;
+std::vector<NamedModel> getNamedModels(const FilterNameFn& filter);
+
+std::string printGeneratedTest(const testing::TestParamInfo<GeneratedTestParam>& info);
+
+#define INSTANTIATE_GENERATED_TEST(TestSuite, filter)                                     \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                             \
+    INSTANTIATE_TEST_SUITE_P(TestGenerated, TestSuite,                                    \
+                             testing::Combine(testing::ValuesIn(getNamedDevices()),       \
+                                              testing::ValuesIn(getNamedModels(filter))), \
+                             printGeneratedTest)
+
+// Tag for the validation tests, instantiated in VtsHalNeuralnetworks.cpp.
+// TODO: Clean up the hierarchy for ValidationTest.
+class ValidationTest : public GeneratedTestBase {};
+
+Model createModel(const test_helper::TestModel& testModel);
+
+void PrepareModel(const std::shared_ptr<IDevice>& device, const Model& model,
+                  std::shared_ptr<IPreparedModel>* preparedModel);
+
+enum class TestKind {
+    // Runs a test model and compares the results to a golden data
+    GENERAL,
+    // Same as GENERAL but sets dimensions for the output tensors to zeros
+    DYNAMIC_SHAPE,
+    // Same as GENERAL but use device memories for inputs and outputs
+    MEMORY_DOMAIN,
+    // Same as GENERAL but use executeFenced for exeuction
+    FENCED_COMPUTE,
+    // Tests if quantized model with TENSOR_QUANT8_ASYMM produces the same result
+    // (OK/SKIPPED/FAILED) as the model with all such tensors converted to
+    // TENSOR_QUANT8_ASYMM_SIGNED.
+    QUANTIZATION_COUPLING,
+    // Runs a test model and verifies that MISSED_DEADLINE_* is returned.
+    INTINITE_LOOP_TIMEOUT
+};
+
+void EvaluatePreparedModel(const std::shared_ptr<IDevice>& device,
+                           const std::shared_ptr<IPreparedModel>& preparedModel,
+                           const test_helper::TestModel& testModel, TestKind testKind);
+
+void waitForSyncFence(int syncFd);
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_AIDL_GENERATED_TEST_HARNESS_H
diff --git a/neuralnetworks/aidl/vts/functional/LogTestCaseToLogcat.h b/neuralnetworks/aidl/vts/functional/LogTestCaseToLogcat.h
new file mode 100644
index 0000000..c9fd432
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/LogTestCaseToLogcat.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_AIDL_LOG_TEST_CASE_TO_LOGCAT_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_LOG_TEST_CASE_TO_LOGCAT_H
+
+#include <android-base/logging.h>
+#include <gtest/gtest.h>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+class LogTestCaseToLogcat : public ::testing::EmptyTestEventListener {
+  public:
+    void OnTestStart(const ::testing::TestInfo& test_info) override {
+        LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+                  << " BEGIN";
+    }
+
+    void OnTestEnd(const ::testing::TestInfo& test_info) override {
+        LOG(INFO) << "[Test Case] " << test_info.test_suite_name() << "." << test_info.name()
+                  << " END";
+    }
+};
+
+}  // namespace aidl::android::hardware::neuralnetworks
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_AIDL_LOG_TEST_CASE_TO_LOGCAT_H
diff --git a/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
new file mode 100644
index 0000000..e8313f1
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/MemoryDomainTests.cpp
@@ -0,0 +1,1231 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
+#include <aidl/android/hardware/graphics/common/PixelFormat.h>
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <gtest/gtest.h>
+
+#include <LegacyUtils.h>
+#include <TestHarness.h>
+#include <Utils.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/hal/aidl/Conversions.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+#include "AidlHalInterfaces.h"
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "MemoryUtils.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using namespace test_helper;
+using implementation::PreparedModelCallback;
+
+namespace {
+
+// An AIDL driver is likely to support at least one of the following operand types.
+const std::vector<TestOperandType> kTestOperandTypeChoicesVector = {
+        TestOperandType::TENSOR_FLOAT32,
+        TestOperandType::TENSOR_FLOAT16,
+        TestOperandType::TENSOR_QUANT8_ASYMM,
+        TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+};
+const auto kTestOperandTypeChoices = testing::ValuesIn(kTestOperandTypeChoicesVector);
+// TODO(b/179270601): restore kNamedDeviceChoices
+
+bool isInChoices(TestOperandType type) {
+    return std::count(kTestOperandTypeChoicesVector.begin(), kTestOperandTypeChoicesVector.end(),
+                      type) > 0;
+}
+
+bool isFloat(TestOperandType type) {
+    CHECK(isInChoices(type));
+    return type == TestOperandType::TENSOR_FLOAT32 || type == TestOperandType::TENSOR_FLOAT16;
+}
+
+// Create placeholder buffers for model constants as well as inputs and outputs.
+// We only care about the size here because we will not check accuracy in validation tests.
+void createDummyData(TestModel* testModel) {
+    for (auto& operand : testModel->main.operands) {
+        if (operand.data != nullptr) continue;
+        switch (operand.lifetime) {
+            case TestOperandLifeTime::SUBGRAPH_INPUT:
+            case TestOperandLifeTime::SUBGRAPH_OUTPUT:
+            case TestOperandLifeTime::CONSTANT_COPY:
+            case TestOperandLifeTime::CONSTANT_REFERENCE: {
+                const uint32_t size = nn::nonExtensionOperandSizeOfData(
+                        static_cast<nn::OperandType>(operand.type), operand.dimensions);
+                operand.data = TestBuffer(size);
+            } break;
+            default:
+                break;
+        }
+    }
+}
+
+TestOperand createInt32Scalar(int32_t value) {
+    return {
+            .type = TestOperandType::INT32,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = TestOperandLifeTime::CONSTANT_COPY,
+            .data = TestBuffer::createFromVector<int32_t>({value}),
+    };
+}
+
+// Construct a test model with multiple CONV_2D operations with the given operand as inputs.
+// The dimensions of the filters are chosen to ensure outputs has the same dimensions as inputs.
+// We choose CONV_2D operation because it is commonly supported by most drivers.
+TestModel createConvModel(const TestOperand& operand, uint32_t numOperations) {
+    CHECK(isInChoices(operand.type));
+
+    TestOperand weight = {.type = operand.type,
+                          .dimensions = {operand.dimensions[3], 3, 3, operand.dimensions[3]},
+                          .numberOfConsumers = 1,
+                          .scale = isFloat(operand.type) ? 0.0f : 1.0f,
+                          .zeroPoint = 0,
+                          .lifetime = TestOperandLifeTime::CONSTANT_COPY};
+
+    TestOperand bias = {
+            .type = isFloat(operand.type) ? operand.type : TestOperandType::TENSOR_INT32,
+            .dimensions = {operand.dimensions[3]},
+            .numberOfConsumers = 1,
+            .scale = operand.scale * weight.scale,
+            .zeroPoint = 0,
+            .lifetime = TestOperandLifeTime::CONSTANT_COPY};
+
+    TestOperand output = operand;
+    output.numberOfConsumers = 0;
+    output.lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT;
+
+    const std::vector<TestOperand> operands = {
+            operand,
+            std::move(weight),
+            std::move(bias),
+            createInt32Scalar(1),  // same padding
+            createInt32Scalar(1),  // width stride
+            createInt32Scalar(1),  // height stride
+            createInt32Scalar(0),  // activation = NONE
+            std::move(output),
+    };
+
+    TestModel model;
+    for (uint32_t i = 0; i < numOperations; i++) {
+        model.main.operands.insert(model.main.operands.end(), operands.begin(), operands.end());
+        const uint32_t inputIndex = operands.size() * i;
+        const uint32_t outputIndex = inputIndex + operands.size() - 1;
+        std::vector<uint32_t> inputs(operands.size() - 1);
+        std::iota(inputs.begin(), inputs.end(), inputIndex);
+        model.main.operations.push_back({.type = TestOperationType::CONV_2D,
+                                         .inputs = std::move(inputs),
+                                         .outputs = {outputIndex}});
+        model.main.inputIndexes.push_back(inputIndex);
+        model.main.outputIndexes.push_back(outputIndex);
+    }
+    createDummyData(&model);
+    return model;
+}
+
+// Construct a test model with a single ADD operation with the given operand as input0 and input1.
+// This is to cover additional cases that the CONV_2D model does not support, e.g. arbitrary input
+// operand rank, scalar input operand. We choose ADD operation because it is commonly supported by
+// most drivers.
+TestModel createSingleAddModel(const TestOperand& operand) {
+    CHECK(isInChoices(operand.type));
+
+    TestOperand act = {
+            .type = TestOperandType::INT32,
+            .dimensions = {},
+            .numberOfConsumers = 1,
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+    };
+
+    TestOperand output = operand;
+    output.numberOfConsumers = 0;
+    output.lifetime = TestOperandLifeTime::SUBGRAPH_OUTPUT;
+
+    TestModel model = {
+            .main =
+                    {
+                            .operands =
+                                    {
+                                            operand,
+                                            operand,
+                                            std::move(act),
+                                            output,
+                                    },
+                            .operations = {{.type = TestOperationType::ADD,
+                                            .inputs = {0, 1, 2},
+                                            .outputs = {3}}},
+                            .inputIndexes = {0, 1, 2},
+                            .outputIndexes = {3},
+                    },
+    };
+    createDummyData(&model);
+    return model;
+}
+
+// A placeholder invalid IPreparedModel class for MemoryDomainAllocateTest.InvalidPreparedModel
+class InvalidPreparedModel : public BnPreparedModel {
+  public:
+    ndk::ScopedAStatus executeSynchronously(const Request&, bool, int64_t, int64_t,
+                                            ExecutionResult*) override {
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+    }
+    ndk::ScopedAStatus executeFenced(const Request&, const std::vector<ndk::ScopedFileDescriptor>&,
+                                     bool, int64_t, int64_t, int64_t,
+                                     FencedExecutionResult*) override {
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+    }
+    ndk::ScopedAStatus configureExecutionBurst(std::shared_ptr<IBurst>*) override {
+        return ndk::ScopedAStatus::fromServiceSpecificError(
+                static_cast<int32_t>(ErrorStatus::GENERAL_FAILURE));
+    }
+};
+
+template <typename... Args>
+std::vector<RequestMemoryPool> createRequestMemoryPools(const Args&... pools) {
+    std::vector<RequestMemoryPool> memoryPools;
+    memoryPools.reserve(sizeof...(Args));
+    // This fold operator calls push_back on each of the function arguments.
+    (memoryPools.push_back(utils::clone(pools).value()), ...);
+    return memoryPools;
+};
+
+}  // namespace
+
+class MemoryDomainTestBase : public testing::Test {
+  protected:
+    MemoryDomainTestBase(std::shared_ptr<IDevice> device, TestOperandType type)
+        : kDevice(std::move(device)),
+          kTestOperandType(type),
+          kTestOperand(kTestOperandMap.at(type)),
+          kTestOperandDataSize(nn::nonExtensionOperandSizeOfData(static_cast<nn::OperandType>(type),
+                                                                 kTestOperand.dimensions)) {}
+
+    void SetUp() override {
+        testing::Test::SetUp();
+        ASSERT_NE(kDevice, nullptr);
+    }
+
+    std::shared_ptr<IPreparedModel> createConvPreparedModel(const TestOperand& testOperand,
+                                                            uint32_t numOperations = 1) {
+        const TestModel testModel = createConvModel(testOperand, numOperations);
+        const Model model = createModel(testModel);
+        std::shared_ptr<IPreparedModel> preparedModel;
+        createPreparedModel(kDevice, model, &preparedModel, /*reportSkipping=*/false);
+        return preparedModel;
+    }
+
+    std::shared_ptr<IPreparedModel> createAddPreparedModel(const TestOperand& testOperand) {
+        const TestModel testModel = createSingleAddModel(testOperand);
+        const Model model = createModel(testModel);
+        std::shared_ptr<IPreparedModel> preparedModel;
+        createPreparedModel(kDevice, model, &preparedModel, /*reportSkipping=*/false);
+        return preparedModel;
+    }
+
+    static const std::map<TestOperandType, TestOperand> kTestOperandMap;
+
+    const std::shared_ptr<IDevice> kDevice;
+    const TestOperandType kTestOperandType;
+    const TestOperand& kTestOperand;
+    const uint32_t kTestOperandDataSize;
+};
+
+const std::map<TestOperandType, TestOperand> MemoryDomainTestBase::kTestOperandMap = {
+        {TestOperandType::TENSOR_FLOAT32,
+         {
+                 .type = TestOperandType::TENSOR_FLOAT32,
+                 .dimensions = {1, 32, 32, 8},
+                 .numberOfConsumers = 1,
+                 .scale = 0.0f,
+                 .zeroPoint = 0,
+                 .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+         }},
+        {TestOperandType::TENSOR_FLOAT16,
+         {
+                 .type = TestOperandType::TENSOR_FLOAT16,
+                 .dimensions = {1, 32, 32, 8},
+                 .numberOfConsumers = 1,
+                 .scale = 0.0f,
+                 .zeroPoint = 0,
+                 .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+         }},
+        {TestOperandType::TENSOR_QUANT8_ASYMM,
+         {
+                 .type = TestOperandType::TENSOR_QUANT8_ASYMM,
+                 .dimensions = {1, 32, 32, 8},
+                 .numberOfConsumers = 1,
+                 .scale = 0.5f,
+                 .zeroPoint = 0,
+                 .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+         }},
+        {TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+         {
+                 .type = TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED,
+                 .dimensions = {1, 32, 32, 8},
+                 .numberOfConsumers = 1,
+                 .scale = 0.5f,
+                 .zeroPoint = 0,
+                 .lifetime = TestOperandLifeTime::SUBGRAPH_INPUT,
+         }},
+};
+
+using MemoryDomainAllocateTestParam = std::tuple<NamedDevice, TestOperandType>;
+class MemoryDomainAllocateTest : public MemoryDomainTestBase,
+                                 public testing::WithParamInterface<MemoryDomainAllocateTestParam> {
+  protected:
+    MemoryDomainAllocateTest()
+        : MemoryDomainTestBase(getData(std::get<NamedDevice>(GetParam())),
+                               std::get<TestOperandType>(GetParam())) {}
+
+    struct AllocateTestArgs {
+        std::vector<int32_t> dimensions;
+        std::vector<std::shared_ptr<IPreparedModel>> preparedModels;
+        std::vector<BufferRole> inputRoles;
+        std::vector<BufferRole> outputRoles;
+    };
+
+    // Validation test for IDevice::allocate. The driver is expected to fail with INVALID_ARGUMENT,
+    // or GENERAL_FAILURE if memory domain is not supported.
+    void validateAllocate(AllocateTestArgs args) {
+        std::vector<IPreparedModelParcel> preparedModelParcels;
+        preparedModelParcels.reserve(args.preparedModels.size());
+        for (const auto& model : args.preparedModels) {
+            preparedModelParcels.push_back({.preparedModel = model});
+        }
+        DeviceBuffer buffer;
+        const auto ret =
+                kDevice->allocate({.dimensions = std::move(args.dimensions)}, preparedModelParcels,
+                                  args.inputRoles, args.outputRoles, &buffer);
+
+        ASSERT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+        ASSERT_TRUE(static_cast<ErrorStatus>(ret.getServiceSpecificError()) ==
+                            ErrorStatus::INVALID_ARGUMENT ||
+                    static_cast<ErrorStatus>(ret.getServiceSpecificError()) ==
+                            ErrorStatus::GENERAL_FAILURE);
+    }
+
+    void testConflictOperands(const std::shared_ptr<IPreparedModel>& model1,
+                              const std::shared_ptr<IPreparedModel>& model2) {
+        validateAllocate({
+                .preparedModels = {model1, model2},
+                .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                               {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+        });
+        validateAllocate({
+                .preparedModels = {model1, model2},
+                .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+                .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+        });
+        validateAllocate({
+                .preparedModels = {model1, model2},
+                .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                                {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+        });
+    }
+};
+
+TEST_P(MemoryDomainAllocateTest, EmptyRole) {
+    // Test with empty prepared models and roles.
+    validateAllocate({});
+
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    // Test again with non-empty prepared models but empty roles.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, NullptrPreparedModel) {
+    // Test with nullptr prepared model as input role.
+    validateAllocate({
+            .preparedModels = {nullptr},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+
+    // Test with nullptr prepared model as output role.
+    validateAllocate({
+            .preparedModels = {nullptr},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidPreparedModel) {
+    std::shared_ptr<InvalidPreparedModel> invalidPreparedModel =
+            ndk::SharedRefBase::make<InvalidPreparedModel>();
+
+    // Test with invalid prepared model as input role.
+    validateAllocate({
+            .preparedModels = {invalidPreparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+
+    // Test with invalid prepared model as output role.
+    validateAllocate({
+            .preparedModels = {invalidPreparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidModelIndex) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    // This should fail, because the model index is out of bound.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+    });
+
+    // This should fail, because the model index is out of bound.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .outputRoles = {{.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidIOIndex) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    // This should fail, because the model only has one input.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
+    });
+
+    // This should fail, because the model only has one output.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 1, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, InvalidProbability) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    for (float invalidFreq : {10.0f, 0.0f, -0.5f}) {
+        // Test with invalid probability for input roles.
+        validateAllocate({
+                .preparedModels = {preparedModel},
+                .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
+        });
+        // Test with invalid probability for output roles.
+        validateAllocate({
+                .preparedModels = {preparedModel},
+                .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = invalidFreq}},
+        });
+    }
+}
+
+TEST_P(MemoryDomainAllocateTest, SameRoleSpecifiedTwice) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    // Same role with same model index.
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                           {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+    validateAllocate({
+            .preparedModels = {preparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                            {.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+
+    // Different model indexes, but logically referring to the same role.
+    validateAllocate({
+            .preparedModels = {preparedModel, preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                           {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+    });
+    validateAllocate({
+            .preparedModels = {preparedModel, preparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f},
+                            {.modelIndex = 1, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictOperandType) {
+    const std::map<TestOperandType, TestOperandType> conflictTypeMap = {
+            {TestOperandType::TENSOR_FLOAT32, TestOperandType::TENSOR_FLOAT16},
+            {TestOperandType::TENSOR_FLOAT16, TestOperandType::TENSOR_FLOAT32},
+            {TestOperandType::TENSOR_QUANT8_ASYMM, TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED},
+            {TestOperandType::TENSOR_QUANT8_ASYMM_SIGNED, TestOperandType::TENSOR_QUANT8_ASYMM},
+    };
+
+    TestOperand conflictTestOperand = kTestOperand;
+    const auto it = conflictTypeMap.find(kTestOperandType);
+    ASSERT_FALSE(it == conflictTypeMap.end());
+    conflictTestOperand.type = it->second;
+
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+    if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+    testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictScale) {
+    if (isFloat(kTestOperandType)) return;
+
+    TestOperand conflictTestOperand = kTestOperand;
+    ASSERT_NE(conflictTestOperand.scale, 1.0f);
+    conflictTestOperand.scale = 1.0f;
+
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+    if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+    testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictZeroPoint) {
+    if (isFloat(kTestOperandType)) return;
+
+    TestOperand conflictTestOperand = kTestOperand;
+    ASSERT_NE(conflictTestOperand.zeroPoint, 10);
+    conflictTestOperand.zeroPoint = 10;
+
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+    if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+    testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankBetweenRoles) {
+    TestOperand conflictTestOperand = kTestOperand;
+    conflictTestOperand.dimensions.pop_back();
+
+    auto preparedModel = createAddPreparedModel(kTestOperand);
+    auto conflictPreparedModel = createAddPreparedModel(conflictTestOperand);
+    if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+    testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictDimensionsBetweenRoles) {
+    TestOperand conflictTestOperand = kTestOperand;
+    conflictTestOperand.dimensions[0] = 4;
+
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto conflictPreparedModel = createConvPreparedModel(conflictTestOperand);
+    if (preparedModel == nullptr || conflictPreparedModel == nullptr) return;
+    testConflictOperands(preparedModel, conflictPreparedModel);
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankBetweenRoleAndDesc) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    auto badDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    badDimensions.pop_back();
+
+    validateAllocate({
+            .dimensions = badDimensions,
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+    validateAllocate({
+            .dimensions = badDimensions,
+            .preparedModels = {preparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictDimensionsBetweenRoleAndDesc) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    auto badDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    badDimensions[0] = 4;
+
+    validateAllocate({
+            .dimensions = badDimensions,
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+    validateAllocate({
+            .dimensions = badDimensions,
+            .preparedModels = {preparedModel},
+            .outputRoles = {{.modelIndex = 0, .ioIndex = 0, .probability = 1.0f}},
+    });
+}
+
+TEST_P(MemoryDomainAllocateTest, ConflictRankWithScalarRole) {
+    auto preparedModel = createAddPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    // This should fail, because the target operand is a scalar but a non-empty dimension is
+    // specified.
+    validateAllocate({
+            .dimensions = {1},
+            .preparedModels = {preparedModel},
+            .inputRoles = {{.modelIndex = 0, .ioIndex = 2, .probability = 1.0f}},
+    });
+}
+
+std::string printMemoryDomainAllocateTest(
+        const testing::TestParamInfo<MemoryDomainAllocateTestParam>& info) {
+    const auto& [namedDevice, operandType] = info.param;
+    const std::string type = toString(static_cast<OperandType>(operandType));
+    return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainAllocateTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainAllocateTest,
+                         testing::Combine(testing::ValuesIn(getNamedDevices()),
+                                          kTestOperandTypeChoices),
+                         printMemoryDomainAllocateTest);
+
+class MemoryDomainCopyTestBase : public MemoryDomainTestBase {
+  protected:
+    MemoryDomainCopyTestBase(std::shared_ptr<IDevice> device, TestOperandType type)
+        : MemoryDomainTestBase(std::move(device), type) {}
+
+    // Allocates device memory for roles of a single prepared model.
+    // Returns {IBuffer, token} if success; returns {nullptr, 0} if not supported.
+    DeviceBuffer allocateBuffer(const std::shared_ptr<IPreparedModel>& preparedModel,
+                                const std::vector<int32_t>& inputIndexes,
+                                const std::vector<int32_t>& outputIndexes,
+                                const std::vector<int32_t>& dimensions) {
+        if (preparedModel == nullptr) {
+            return {.buffer = nullptr, .token = 0};
+        }
+
+        std::vector<BufferRole> inputRoles(inputIndexes.size()), outputRoles(outputIndexes.size());
+        auto trans = [](int32_t ind) -> BufferRole {
+            return {.modelIndex = 0, .ioIndex = ind, .probability = 1.0f};
+        };
+        std::transform(inputIndexes.begin(), inputIndexes.end(), inputRoles.begin(), trans);
+        std::transform(outputIndexes.begin(), outputIndexes.end(), outputRoles.begin(), trans);
+
+        IPreparedModelParcel parcel;
+        parcel.preparedModel = preparedModel;
+
+        DeviceBuffer buffer;
+
+        const auto ret = kDevice->allocate({.dimensions = dimensions}, {parcel}, inputRoles,
+                                           outputRoles, &buffer);
+
+        if (!ret.isOk()) {
+            EXPECT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            EXPECT_EQ(static_cast<ErrorStatus>(ret.getServiceSpecificError()),
+                      ErrorStatus::GENERAL_FAILURE);
+            return DeviceBuffer{
+                    .buffer = nullptr,
+                    .token = 0,
+            };
+        }
+
+        EXPECT_NE(buffer.buffer, nullptr);
+        EXPECT_GT(buffer.token, 0);
+
+        return buffer;
+    }
+
+    DeviceBuffer allocateBuffer(const std::shared_ptr<IPreparedModel>& preparedModel,
+                                const std::vector<int32_t>& inputIndexes,
+                                const std::vector<int32_t>& outputIndexes) {
+        return allocateBuffer(preparedModel, inputIndexes, outputIndexes, {});
+    }
+
+    size_t getSize(const Memory& memory) {
+        switch (memory.getTag()) {
+            case Memory::Tag::ashmem:
+                return memory.get<Memory::Tag::ashmem>().size;
+            case Memory::Tag::mappableFile:
+                return memory.get<Memory::Tag::mappableFile>().length;
+            case Memory::Tag::hardwareBuffer: {
+                const auto& hardwareBuffer = memory.get<Memory::Tag::hardwareBuffer>();
+                const bool isBlob =
+                        hardwareBuffer.description.format == graphics::common::PixelFormat::BLOB;
+                return isBlob ? hardwareBuffer.description.width : 0;
+            }
+        }
+        return 0;
+    }
+
+    Memory allocateSharedMemory(uint32_t size) {
+        const auto sharedMemory = nn::createSharedMemory(size).value();
+        auto memory = utils::convert(sharedMemory).value();
+        EXPECT_EQ(getSize(memory), size);
+        return memory;
+    }
+
+    void testCopyFrom(const std::shared_ptr<IBuffer>& buffer, const Memory& memory,
+                      const std::vector<int32_t>& dimensions, ErrorStatus expectedStatus) {
+        const auto ret = buffer->copyFrom(memory, dimensions);
+        if (expectedStatus == ErrorStatus::NONE) {
+            ASSERT_TRUE(ret.isOk());
+        } else {
+            ASSERT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            ASSERT_EQ(expectedStatus, static_cast<ErrorStatus>(ret.getServiceSpecificError()));
+        }
+    }
+
+    void testCopyTo(const std::shared_ptr<IBuffer>& buffer, const Memory& memory,
+                    ErrorStatus expectedStatus) {
+        const auto ret = buffer->copyTo(memory);
+        if (expectedStatus == ErrorStatus::NONE) {
+            ASSERT_TRUE(ret.isOk());
+        } else {
+            ASSERT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            ASSERT_EQ(expectedStatus, static_cast<ErrorStatus>(ret.getServiceSpecificError()));
+        }
+    }
+
+    void initializeDeviceMemory(const std::shared_ptr<IBuffer>& buffer) {
+        Memory memory = allocateSharedMemory(kTestOperandDataSize);
+        ASSERT_EQ(getSize(memory), kTestOperandDataSize);
+        testCopyFrom(buffer, memory, utils::toSigned(kTestOperand.dimensions).value(),
+                     ErrorStatus::NONE);
+    }
+};
+
+using MemoryDomainCopyTestParam = std::tuple<NamedDevice, TestOperandType>;
+class MemoryDomainCopyTest : public MemoryDomainCopyTestBase,
+                             public testing::WithParamInterface<MemoryDomainCopyTestParam> {
+  protected:
+    MemoryDomainCopyTest()
+        : MemoryDomainCopyTestBase(getData(std::get<NamedDevice>(GetParam())),
+                                   std::get<TestOperandType>(GetParam())) {}
+};
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidMemorySize) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+    Memory badMemory1 = allocateSharedMemory(badMemorySize1);
+    Memory badMemory2 = allocateSharedMemory(badMemorySize2);
+    testCopyFrom(buffer, badMemory1, {}, ErrorStatus::INVALID_ARGUMENT);
+    testCopyFrom(buffer, badMemory2, {}, ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidMemorySize_DynamicShape) {
+    TestOperand testOperand = kTestOperand;
+    testOperand.dimensions[0] = 0;
+    auto preparedModel = createConvPreparedModel(testOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+    Memory badMemory1 = allocateSharedMemory(badMemorySize1);
+    Memory badMemory2 = allocateSharedMemory(badMemorySize2);
+    Memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+    const auto goodDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    auto badDimensions = goodDimensions;
+    badDimensions[0] = 2;
+
+    testCopyFrom(buffer, badMemory1, goodDimensions, ErrorStatus::INVALID_ARGUMENT);
+    testCopyFrom(buffer, badMemory2, goodDimensions, ErrorStatus::INVALID_ARGUMENT);
+    testCopyFrom(buffer, goodMemory, goodDimensions, ErrorStatus::NONE);
+    testCopyFrom(buffer, goodMemory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidDimensions) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    Memory memory = allocateSharedMemory(kTestOperandDataSize);
+
+    const auto goodDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    std::vector<int32_t> badDimensions = goodDimensions;
+    badDimensions.pop_back();
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    badDimensions = goodDimensions;
+    badDimensions[0] = 2;
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    badDimensions = goodDimensions;
+    badDimensions[0] = 0;
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    testCopyFrom(buffer, memory, {}, ErrorStatus::NONE);
+    testCopyFrom(buffer, memory, goodDimensions, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyFrom_InvalidDimensions_DynamicShape) {
+    TestOperand testOperand = kTestOperand;
+    testOperand.dimensions[0] = 0;
+    auto preparedModel = createConvPreparedModel(testOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    Memory memory = allocateSharedMemory(kTestOperandDataSize);
+
+    const auto goodDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    std::vector<int32_t> badDimensions = goodDimensions;
+    badDimensions.pop_back();
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    badDimensions = goodDimensions;
+    badDimensions[0] = 2;
+    badDimensions[3] = 4;
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    badDimensions = goodDimensions;
+    badDimensions[0] = 1;
+    badDimensions[3] = 0;
+    testCopyFrom(buffer, memory, badDimensions, ErrorStatus::INVALID_ARGUMENT);
+
+    testCopyFrom(buffer, memory, {}, ErrorStatus::INVALID_ARGUMENT);
+    testCopyFrom(buffer, memory, goodDimensions, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_UninitializedMemory) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    Memory memory = allocateSharedMemory(kTestOperandDataSize);
+    testCopyTo(buffer, memory, ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_InvalidMemorySize) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+    Memory badMemory1 = allocateSharedMemory(badMemorySize1);
+    Memory badMemory2 = allocateSharedMemory(badMemorySize2);
+    Memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+    initializeDeviceMemory(buffer);
+    testCopyTo(buffer, badMemory1, ErrorStatus::INVALID_ARGUMENT);
+    testCopyTo(buffer, badMemory2, ErrorStatus::INVALID_ARGUMENT);
+    testCopyTo(buffer, goodMemory, ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainCopyTest, CopyTo_InvalidMemorySize_DynamicShape) {
+    TestOperand testOperand = kTestOperand;
+    testOperand.dimensions[0] = 0;
+    auto preparedModel = createConvPreparedModel(testOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    uint32_t badMemorySize1 = kTestOperandDataSize / 2, badMemorySize2 = kTestOperandDataSize * 2;
+    Memory badMemory1 = allocateSharedMemory(badMemorySize1);
+    Memory badMemory2 = allocateSharedMemory(badMemorySize2);
+    Memory goodMemory = allocateSharedMemory(kTestOperandDataSize);
+
+    initializeDeviceMemory(buffer);
+    testCopyTo(buffer, badMemory1, ErrorStatus::INVALID_ARGUMENT);
+    testCopyTo(buffer, badMemory2, ErrorStatus::INVALID_ARGUMENT);
+    testCopyTo(buffer, goodMemory, ErrorStatus::NONE);
+}
+
+std::string printMemoryDomainCopyTest(
+        const testing::TestParamInfo<MemoryDomainCopyTestParam>& info) {
+    const auto& [namedDevice, operandType] = info.param;
+    const std::string type = toString(static_cast<OperandType>(operandType));
+    return gtestCompliantName(getName(namedDevice) + "_" + type);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainCopyTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainCopyTest,
+                         testing::Combine(testing::ValuesIn(getNamedDevices()),
+                                          kTestOperandTypeChoices),
+                         printMemoryDomainCopyTest);
+
+using MemoryDomainExecutionTestParam = std::tuple<NamedDevice, TestOperandType, Executor>;
+class MemoryDomainExecutionTest
+    : public MemoryDomainCopyTestBase,
+      public testing::WithParamInterface<MemoryDomainExecutionTestParam> {
+  protected:
+    MemoryDomainExecutionTest()
+        : MemoryDomainCopyTestBase(getData(std::get<NamedDevice>(GetParam())),
+                                   std::get<TestOperandType>(GetParam())) {}
+
+    RequestMemoryPool createSharedMemoryPool(uint32_t size) {
+        return RequestMemoryPool(allocateSharedMemory(size));
+    }
+
+    RequestMemoryPool createDeviceMemoryPool(uint32_t token) {
+        return RequestMemoryPool(static_cast<int32_t>(token));
+    }
+
+    void testExecution(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request,
+                       ErrorStatus expectedStatus) {
+        switch (kExecutor) {
+            case Executor::SYNC:
+                EXPECT_EQ(executeSync(preparedModel, request), expectedStatus);
+                break;
+            case Executor::BURST:
+                EXPECT_EQ(executeBurst(preparedModel, request), expectedStatus);
+                break;
+            case Executor::FENCED:
+                EXPECT_EQ(executeFenced(preparedModel, request), expectedStatus);
+                break;
+            default:
+                ASSERT_TRUE(false);
+        }
+    }
+
+    ErrorStatus executeSync(const std::shared_ptr<IPreparedModel>& preparedModel,
+                            const Request& request) {
+        ExecutionResult executionResult;
+        const auto ret = preparedModel->executeSynchronously(
+                request, false, kNoDeadline, kOmittedTimeoutDuration, &executionResult);
+
+        if (!ret.isOk()) {
+            EXPECT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            return static_cast<ErrorStatus>(ret.getServiceSpecificError());
+        }
+        const ErrorStatus executionStatus = executionResult.outputSufficientSize
+                                                    ? ErrorStatus::NONE
+                                                    : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+        EXPECT_EQ(executionResult.timing, kNoTiming);
+        return executionStatus;
+    }
+
+    ErrorStatus executeFenced(const std::shared_ptr<IPreparedModel>& preparedModel,
+                              const Request& request) {
+        FencedExecutionResult executionResult;
+        const auto ret = preparedModel->executeFenced(request, {}, false, kNoDeadline,
+                                                      kOmittedTimeoutDuration, kNoDuration,
+                                                      &executionResult);
+        if (!ret.isOk()) {
+            EXPECT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            return static_cast<ErrorStatus>(ret.getServiceSpecificError());
+        }
+        if (executionResult.syncFence.get() != -1) {
+            waitForSyncFence(executionResult.syncFence.get());
+        }
+        EXPECT_NE(executionResult.callback, nullptr);
+
+        ErrorStatus executionStatus = ErrorStatus::GENERAL_FAILURE;
+        Timing time = kNoTiming;
+        Timing timeFenced = kNoTiming;
+        const auto retExecutionInfo =
+                executionResult.callback->getExecutionInfo(&time, &timeFenced, &executionStatus);
+        EXPECT_TRUE(retExecutionInfo.isOk());
+        EXPECT_EQ(time, kNoTiming);
+        return executionStatus;
+    }
+
+    ErrorStatus executeBurst(const std::shared_ptr<IPreparedModel>& preparedModel,
+                             const Request& request) {
+        // create burst
+        std::shared_ptr<IBurst> burst;
+        auto ret = preparedModel->configureExecutionBurst(&burst);
+        EXPECT_TRUE(ret.isOk()) << ret.getDescription();
+        EXPECT_NE(nullptr, burst.get());
+        if (!ret.isOk() || burst.get() == nullptr) {
+            return ErrorStatus::GENERAL_FAILURE;
+        }
+
+        // use -1 for all memory identifier tokens
+        const std::vector<int64_t> slots(request.pools.size(), -1);
+
+        ExecutionResult executionResult;
+        ret = burst->executeSynchronously(request, slots, false, kNoDeadline,
+                                          kOmittedTimeoutDuration, &executionResult);
+
+        if (!ret.isOk()) {
+            EXPECT_EQ(ret.getExceptionCode(), EX_SERVICE_SPECIFIC);
+            return static_cast<ErrorStatus>(ret.getServiceSpecificError());
+        }
+        const ErrorStatus executionStatus = executionResult.outputSufficientSize
+                                                    ? ErrorStatus::NONE
+                                                    : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE;
+        EXPECT_EQ(executionResult.timing, kNoTiming);
+        return executionStatus;
+    }
+
+    const Executor kExecutor = std::get<Executor>(GetParam());
+};
+
+TEST_P(MemoryDomainExecutionTest, InvalidToken) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    if (preparedModel == nullptr) return;
+
+    RequestMemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool badDeviceMemory1 = createDeviceMemoryPool(0);    // Invalid token.
+    RequestMemoryPool badDeviceMemory2 = createDeviceMemoryPool(100);  // Unknown token.
+    RequestArgument sharedMemoryArg = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, badDeviceMemory1)},
+                  ErrorStatus::INVALID_ARGUMENT);
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, badDeviceMemory2)},
+                  ErrorStatus::INVALID_ARGUMENT);
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, badDeviceMemory1)},
+                  ErrorStatus::INVALID_ARGUMENT);
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, badDeviceMemory2)},
+                  ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidPreparedModel) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+    auto badPreparedModel = createConvPreparedModel(kTestOperand);
+    if (badPreparedModel == nullptr) return;
+
+    RequestMemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(token);
+    RequestArgument sharedMemoryArg = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+    // This should fail, because the buffer is not allocated for badPreparedModel.
+    initializeDeviceMemory(buffer);
+    testExecution(badPreparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+    testExecution(badPreparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidIOIndex) {
+    auto preparedModel = createConvPreparedModel(kTestOperand, 2);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {});
+    if (buffer == nullptr) return;
+
+    RequestMemoryPool sharedMemory1 = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool sharedMemory2 = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool sharedMemory3 = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(token);
+    RequestArgument sharedMemoryArg1 = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument sharedMemoryArg2 = {
+            .location = {.poolIndex = 1, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument sharedMemoryArg3 = {
+            .location = {.poolIndex = 2, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 3}};
+
+    // This should fail, because the device memory is not allocated for input 1.
+    initializeDeviceMemory(buffer);
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg1, deviceMemoryArg},
+                   .outputs = {sharedMemoryArg2, sharedMemoryArg3},
+                   .pools = createRequestMemoryPools(sharedMemory1, sharedMemory2, sharedMemory3,
+                                                     deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    // This should fail, because the device memory is not allocated for output 1.
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg1, sharedMemoryArg2},
+                   .outputs = {sharedMemoryArg3, deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory1, sharedMemory2, sharedMemory3,
+                                                     deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidIOType) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [inputBuffer, inputToken] = allocateBuffer(preparedModel, {0}, {});
+    auto [outputBuffer, outputToken] = allocateBuffer(preparedModel, {}, {0});
+    if (inputBuffer == nullptr || outputBuffer == nullptr) return;
+
+    RequestMemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(inputToken);
+    RequestArgument sharedMemoryArg = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+    // This should fail, because the device memory is allocated for input but used as output.
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    // This should fail, because the device memory is allocated for output but used as input.
+    deviceMemory.set<RequestMemoryPool::Tag::token>(outputToken);
+    initializeDeviceMemory(outputBuffer);
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST_P(MemoryDomainExecutionTest, UninitializedMemory) {
+    auto preparedModel = createConvPreparedModel(kTestOperand);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0}, {0});
+    if (buffer == nullptr) return;
+
+    RequestMemoryPool sharedMemory = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(token);
+    RequestArgument sharedMemoryArg = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+
+    // This should fail, because the device memory is not initialized.
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::GENERAL_FAILURE);
+
+    // This should initialize the device memory.
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::NONE);
+
+    // Test again with initialized device memory.
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainExecutionTest, SameRequestMultipleRoles) {
+    auto preparedModel = createConvPreparedModel(kTestOperand, 2);
+    auto [buffer, token] = allocateBuffer(preparedModel, {0, 1}, {0, 1});
+    if (buffer == nullptr) return;
+
+    RequestMemoryPool sharedMemory1 = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool sharedMemory2 = createSharedMemoryPool(kTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(token);
+    RequestArgument sharedMemoryArg1 = {
+            .location = {.poolIndex = 0, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument sharedMemoryArg2 = {
+            .location = {.poolIndex = 1, .offset = 0, .length = kTestOperandDataSize}};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 2}};
+
+    // This should fail, because the same device memory cannot be used for both input and output.
+    initializeDeviceMemory(buffer);
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg, sharedMemoryArg1},
+                   .outputs = {deviceMemoryArg, sharedMemoryArg2},
+                   .pools = createRequestMemoryPools(sharedMemory1, sharedMemory2, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    // This should fail, because the same device memory cannot be used for multiple outputs.
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg1, sharedMemoryArg2},
+                   .outputs = {deviceMemoryArg, deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory1, sharedMemory2, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    // The same device memory can be used for multiple inputs.
+    initializeDeviceMemory(buffer);
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArg, deviceMemoryArg},
+                   .outputs = {sharedMemoryArg1, sharedMemoryArg2},
+                   .pools = createRequestMemoryPools(sharedMemory1, sharedMemory2, deviceMemory)},
+                  ErrorStatus::NONE);
+}
+
+TEST_P(MemoryDomainExecutionTest, InvalidDimensions) {
+    // FENCED execution does not support dynamic shape.
+    if (kExecutor == Executor::FENCED) return;
+
+    TestOperand testOperand = kTestOperand;
+    testOperand.dimensions[0] = 0;
+    auto preparedModel = createConvPreparedModel(testOperand);
+    auto deviceBuffer = allocateBuffer(preparedModel, {0}, {0},
+                                       utils::toSigned(kTestOperand.dimensions).value());
+    if (deviceBuffer.buffer == nullptr) return;
+
+    // Use an incompatible dimension and make sure the length matches with the bad dimension.
+    auto badDimensions = utils::toSigned(kTestOperand.dimensions).value();
+    badDimensions[0] = 2;
+    const uint32_t badTestOperandDataSize = kTestOperandDataSize * 2;
+
+    RequestMemoryPool sharedMemory = createSharedMemoryPool(badTestOperandDataSize);
+    RequestMemoryPool deviceMemory = createDeviceMemoryPool(deviceBuffer.token);
+    RequestArgument sharedMemoryArg = {
+            .location = {.poolIndex = 0, .offset = 0, .length = badTestOperandDataSize},
+            .dimensions = badDimensions};
+    RequestArgument deviceMemoryArg = {.location = {.poolIndex = 1}};
+    RequestArgument deviceMemoryArgWithBadDimensions = {.location = {.poolIndex = 1},
+                                                        .dimensions = badDimensions};
+
+    initializeDeviceMemory(deviceBuffer.buffer);
+    testExecution(preparedModel,
+                  {.inputs = {deviceMemoryArgWithBadDimensions},
+                   .outputs = {sharedMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArgWithBadDimensions},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::INVALID_ARGUMENT);
+
+    testExecution(preparedModel,
+                  {.inputs = {sharedMemoryArg},
+                   .outputs = {deviceMemoryArg},
+                   .pools = createRequestMemoryPools(sharedMemory, deviceMemory)},
+                  ErrorStatus::GENERAL_FAILURE);
+}
+
+const auto kExecutorChoices = testing::Values(Executor::SYNC, Executor::BURST, Executor::FENCED);
+
+std::string printMemoryDomainExecutionTest(
+        const testing::TestParamInfo<MemoryDomainExecutionTestParam>& info) {
+    const auto& [namedDevice, operandType, executor] = info.param;
+    const std::string type = toString(static_cast<OperandType>(operandType));
+    const std::string executorStr = toString(executor);
+    return gtestCompliantName(getName(namedDevice) + "_" + type + "_" + executorStr);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MemoryDomainExecutionTest);
+INSTANTIATE_TEST_SUITE_P(TestMemoryDomain, MemoryDomainExecutionTest,
+                         testing::Combine(testing::ValuesIn(getNamedDevices()),
+                                          kTestOperandTypeChoices, kExecutorChoices),
+                         printMemoryDomainExecutionTest);
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
new file mode 100644
index 0000000..bbba887
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/QualityOfServiceTests.cpp
@@ -0,0 +1,317 @@
+/*
+ * 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.
+ */
+
+#include <android-base/chrono_utils.h>
+#include <android/binder_enums.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_status.h>
+#include <nnapi/hal/aidl/Conversions.h>
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using implementation::PreparedModelCallback;
+using test_helper::TestBuffer;
+using test_helper::TestModel;
+
+enum class DeadlineBoundType { NOW, UNLIMITED, SHORT };
+constexpr std::array<DeadlineBoundType, 3> deadlineBounds = {
+        DeadlineBoundType::NOW, DeadlineBoundType::UNLIMITED, DeadlineBoundType::SHORT};
+std::string toString(DeadlineBoundType type) {
+    switch (type) {
+        case DeadlineBoundType::NOW:
+            return "NOW";
+        case DeadlineBoundType::UNLIMITED:
+            return "UNLIMITED";
+        case DeadlineBoundType::SHORT:
+            return "SHORT";
+    }
+    LOG(FATAL) << "Unrecognized DeadlineBoundType: " << static_cast<int>(type);
+    return {};
+}
+
+constexpr auto kShortDuration = std::chrono::milliseconds{5};
+
+using Results = std::tuple<ErrorStatus, std::vector<OutputShape>, Timing>;
+using MaybeResults = std::optional<Results>;
+
+using ExecutionFunction =
+        std::function<MaybeResults(const std::shared_ptr<IPreparedModel>& preparedModel,
+                                   const Request& request, int64_t deadlineNs)>;
+
+static int64_t makeDeadline(DeadlineBoundType deadlineBoundType) {
+    const auto getNanosecondsSinceEpoch = [](const auto& time) -> int64_t {
+        const auto timeSinceEpoch = time.time_since_epoch();
+        return std::chrono::duration_cast<std::chrono::nanoseconds>(timeSinceEpoch).count();
+    };
+
+    ::android::base::boot_clock::time_point timePoint;
+    switch (deadlineBoundType) {
+        case DeadlineBoundType::NOW:
+            timePoint = ::android::base::boot_clock::now();
+            break;
+        case DeadlineBoundType::UNLIMITED:
+            timePoint = ::android::base::boot_clock::time_point::max();
+            break;
+        case DeadlineBoundType::SHORT:
+            timePoint = ::android::base::boot_clock::now() + kShortDuration;
+            break;
+    }
+
+    return getNanosecondsSinceEpoch(timePoint);
+}
+
+void runPrepareModelTest(const std::shared_ptr<IDevice>& device, const Model& model,
+                         Priority priority, std::optional<DeadlineBoundType> deadlineBound) {
+    int64_t deadlineNs = kNoDeadline;
+    if (deadlineBound.has_value()) {
+        deadlineNs = makeDeadline(deadlineBound.value());
+    }
+
+    // see if service can handle model
+    std::vector<bool> supportedOps;
+    const auto supportedCallStatus = device->getSupportedOperations(model, &supportedOps);
+    ASSERT_TRUE(supportedCallStatus.isOk());
+    ASSERT_NE(0ul, supportedOps.size());
+    const bool fullySupportsModel =
+            std::all_of(supportedOps.begin(), supportedOps.end(), [](bool valid) { return valid; });
+
+    // launch prepare model
+    const std::shared_ptr<PreparedModelCallback> preparedModelCallback =
+            ndk::SharedRefBase::make<PreparedModelCallback>();
+    const auto prepareLaunchStatus =
+            device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, priority,
+                                 deadlineNs, {}, {}, kEmptyCacheToken, preparedModelCallback);
+    ASSERT_TRUE(prepareLaunchStatus.isOk())
+            << "prepareLaunchStatus: " << prepareLaunchStatus.getDescription();
+
+    // retrieve prepared model
+    preparedModelCallback->wait();
+    const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+    const std::shared_ptr<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel();
+
+    // The getSupportedOperations call returns a list of operations that are guaranteed not to fail
+    // if prepareModel is called, and 'fullySupportsModel' is true i.f.f. the entire model is
+    // guaranteed. If a driver has any doubt that it can prepare an operation, it must return false.
+    // So here, if a driver isn't sure if it can support an operation, but reports that it
+    // successfully prepared the model, the test can continue.
+    if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
+        ASSERT_EQ(nullptr, preparedModel.get());
+        return;
+    }
+
+    // verify return status
+    if (!deadlineBound.has_value()) {
+        EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+    } else {
+        switch (deadlineBound.value()) {
+            case DeadlineBoundType::NOW:
+            case DeadlineBoundType::SHORT:
+                // Either the driver successfully completed the task or it
+                // aborted and returned MISSED_DEADLINE_*.
+                EXPECT_TRUE(prepareReturnStatus == ErrorStatus::NONE ||
+                            prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+                            prepareReturnStatus == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+                break;
+            case DeadlineBoundType::UNLIMITED:
+                // If an unlimited deadline is supplied, we expect the execution to
+                // proceed normally. In this case, check it normally by breaking out
+                // of the switch statement.
+                EXPECT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+                break;
+        }
+    }
+    ASSERT_EQ(prepareReturnStatus == ErrorStatus::NONE, preparedModel.get() != nullptr);
+}
+
+void runPrepareModelTests(const std::shared_ptr<IDevice>& device, const Model& model) {
+    // test priority
+    for (auto priority : ndk::enum_range<Priority>{}) {
+        SCOPED_TRACE("priority: " + toString(priority));
+        if (priority == kDefaultPriority) continue;
+        runPrepareModelTest(device, model, priority, {});
+    }
+
+    // test deadline
+    for (auto deadlineBound : deadlineBounds) {
+        SCOPED_TRACE("deadlineBound: " + toString(deadlineBound));
+        runPrepareModelTest(device, model, kDefaultPriority, deadlineBound);
+    }
+}
+
+static MaybeResults executeSynchronously(const std::shared_ptr<IPreparedModel>& preparedModel,
+                                         const Request& request, int64_t deadlineNs) {
+    SCOPED_TRACE("synchronous");
+    const bool measure = false;
+
+    // run execution
+    ExecutionResult executionResult;
+    const auto ret = preparedModel->executeSynchronously(request, measure, deadlineNs,
+                                                         kOmittedTimeoutDuration, &executionResult);
+    EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+            << ret.getDescription();
+    if (!ret.isOk()) {
+        if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
+            return std::nullopt;
+        }
+        return MaybeResults(
+                {static_cast<ErrorStatus>(ret.getServiceSpecificError()), {}, kNoTiming});
+    }
+
+    // return results
+    return MaybeResults({executionResult.outputSufficientSize
+                                 ? ErrorStatus::NONE
+                                 : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
+                         std::move(executionResult.outputShapes), executionResult.timing});
+}
+
+static MaybeResults executeBurst(const std::shared_ptr<IPreparedModel>& preparedModel,
+                                 const Request& request, int64_t deadlineNs) {
+    SCOPED_TRACE("burst");
+    const bool measure = false;
+
+    // create burst
+    std::shared_ptr<IBurst> burst;
+    auto ret = preparedModel->configureExecutionBurst(&burst);
+    EXPECT_TRUE(ret.isOk()) << ret.getDescription();
+    EXPECT_NE(nullptr, burst.get());
+    if (!ret.isOk() || burst.get() == nullptr) {
+        return std::nullopt;
+    }
+
+    // use -1 for all memory identifier tokens
+    const std::vector<int64_t> slots(request.pools.size(), -1);
+
+    // run execution
+    ExecutionResult executionResult;
+    ret = burst->executeSynchronously(request, slots, measure, deadlineNs, kOmittedTimeoutDuration,
+                                      &executionResult);
+    EXPECT_TRUE(ret.isOk() || ret.getExceptionCode() == EX_SERVICE_SPECIFIC)
+            << ret.getDescription();
+    if (!ret.isOk()) {
+        if (ret.getExceptionCode() != EX_SERVICE_SPECIFIC) {
+            return std::nullopt;
+        }
+        return MaybeResults(
+                {static_cast<ErrorStatus>(ret.getServiceSpecificError()), {}, kNoTiming});
+    }
+
+    // return results
+    return MaybeResults({executionResult.outputSufficientSize
+                                 ? ErrorStatus::NONE
+                                 : ErrorStatus::OUTPUT_INSUFFICIENT_SIZE,
+                         std::move(executionResult.outputShapes), executionResult.timing});
+}
+
+void runExecutionTest(const std::shared_ptr<IPreparedModel>& preparedModel,
+                      const TestModel& testModel, const Request& request,
+                      const ExecutionContext& context, bool synchronous,
+                      DeadlineBoundType deadlineBound) {
+    const ExecutionFunction execute = synchronous ? executeSynchronously : executeBurst;
+    const auto deadlineNs = makeDeadline(deadlineBound);
+
+    // Perform execution and unpack results.
+    const auto results = execute(preparedModel, request, deadlineNs);
+    if (!results.has_value()) return;
+    const auto& [status, outputShapes, timing] = results.value();
+
+    // Verify no timing information was returned
+    EXPECT_EQ(timing, kNoTiming);
+
+    // Validate deadline information if applicable.
+    switch (deadlineBound) {
+        case DeadlineBoundType::NOW:
+        case DeadlineBoundType::SHORT:
+            // Either the driver successfully completed the task or it
+            // aborted and returned MISSED_DEADLINE_*.
+            ASSERT_TRUE(status == ErrorStatus::NONE ||
+                        status == ErrorStatus::MISSED_DEADLINE_TRANSIENT ||
+                        status == ErrorStatus::MISSED_DEADLINE_PERSISTENT);
+            break;
+        case DeadlineBoundType::UNLIMITED:
+            // If an unlimited deadline is supplied, we expect the execution to
+            // proceed normally. In this case, check it normally by breaking out
+            // of the switch statement.
+            ASSERT_EQ(ErrorStatus::NONE, status);
+            break;
+    }
+
+    // If the model output operands are fully specified, outputShapes must be either
+    // either empty, or have the same number of elements as the number of outputs.
+    ASSERT_TRUE(outputShapes.size() == 0 ||
+                outputShapes.size() == testModel.main.outputIndexes.size());
+
+    // Go through all outputs, check returned output shapes.
+    for (uint32_t i = 0; i < outputShapes.size(); i++) {
+        EXPECT_TRUE(outputShapes[i].isSufficient);
+        const auto expect =
+                utils::toSigned(testModel.main.operands[testModel.main.outputIndexes[i]].dimensions)
+                        .value();
+        const std::vector<int32_t>& actual = outputShapes[i].dimensions;
+        EXPECT_EQ(expect, actual);
+    }
+
+    // Retrieve execution results.
+    const std::vector<TestBuffer> outputs = context.getOutputBuffers(request);
+
+    // We want "close-enough" results.
+    if (status == ErrorStatus::NONE) {
+        checkResults(testModel, outputs);
+    }
+}
+
+void runExecutionTests(const std::shared_ptr<IPreparedModel>& preparedModel,
+                       const TestModel& testModel, const Request& request,
+                       const ExecutionContext& context) {
+    for (bool synchronous : {false, true}) {
+        for (auto deadlineBound : deadlineBounds) {
+            runExecutionTest(preparedModel, testModel, request, context, synchronous,
+                             deadlineBound);
+        }
+    }
+}
+
+void runTests(const std::shared_ptr<IDevice>& device, const TestModel& testModel) {
+    // setup
+    const Model model = createModel(testModel);
+
+    // run prepare model tests
+    runPrepareModelTests(device, model);
+
+    // prepare model
+    std::shared_ptr<IPreparedModel> preparedModel;
+    createPreparedModel(device, model, &preparedModel);
+    if (preparedModel == nullptr) return;
+
+    // run execution tests
+    ExecutionContext context;
+    const Request request = context.createRequest(testModel);
+    runExecutionTests(preparedModel, testModel, request, context);
+}
+
+class DeadlineTest : public GeneratedTestBase {};
+
+TEST_P(DeadlineTest, Test) {
+    runTests(kDevice, kTestModel);
+}
+
+INSTANTIATE_GENERATED_TEST(DeadlineTest,
+                           [](const TestModel& testModel) { return !testModel.expectFailure; });
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/TestAssertions.cpp b/neuralnetworks/aidl/vts/functional/TestAssertions.cpp
new file mode 100644
index 0000000..a9e9456
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/TestAssertions.cpp
@@ -0,0 +1,153 @@
+/*
+ * 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.
+ */
+
+#include <aidl/android/hardware/neuralnetworks/IPreparedModel.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/OperationType.h>
+
+#include <ControlFlow.h>
+#include <TestHarness.h>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+namespace nn = ::android::nn;
+
+static_assert(static_cast<uint64_t>(IPreparedModel::DEFAULT_LOOP_TIMEOUT_DURATION_NS) ==
+              nn::operation_while::kTimeoutNsDefault);
+static_assert(static_cast<uint64_t>(IPreparedModel::MAXIMUM_LOOP_TIMEOUT_DURATION_NS) ==
+              nn::operation_while::kTimeoutNsMaximum);
+
+// Make sure that the HIDL enums are compatible with the values defined in
+// frameworks/ml/nn/tools/test_generator/test_harness/include/TestHarness.h.
+using namespace test_helper;
+#define CHECK_TEST_ENUM(EnumType, enumValue) \
+    static_assert(static_cast<EnumType>(Test##EnumType::enumValue) == EnumType::enumValue)
+
+CHECK_TEST_ENUM(OperandType, FLOAT32);
+CHECK_TEST_ENUM(OperandType, INT32);
+CHECK_TEST_ENUM(OperandType, UINT32);
+CHECK_TEST_ENUM(OperandType, TENSOR_FLOAT32);
+CHECK_TEST_ENUM(OperandType, TENSOR_INT32);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT8_ASYMM);
+CHECK_TEST_ENUM(OperandType, BOOL);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT16_SYMM);
+CHECK_TEST_ENUM(OperandType, TENSOR_FLOAT16);
+CHECK_TEST_ENUM(OperandType, TENSOR_BOOL8);
+CHECK_TEST_ENUM(OperandType, FLOAT16);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT8_SYMM_PER_CHANNEL);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT16_ASYMM);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT8_SYMM);
+CHECK_TEST_ENUM(OperandType, TENSOR_QUANT8_ASYMM_SIGNED);
+
+CHECK_TEST_ENUM(OperationType, ADD);
+CHECK_TEST_ENUM(OperationType, AVERAGE_POOL_2D);
+CHECK_TEST_ENUM(OperationType, CONCATENATION);
+CHECK_TEST_ENUM(OperationType, CONV_2D);
+CHECK_TEST_ENUM(OperationType, DEPTHWISE_CONV_2D);
+CHECK_TEST_ENUM(OperationType, DEPTH_TO_SPACE);
+CHECK_TEST_ENUM(OperationType, DEQUANTIZE);
+CHECK_TEST_ENUM(OperationType, EMBEDDING_LOOKUP);
+CHECK_TEST_ENUM(OperationType, FLOOR);
+CHECK_TEST_ENUM(OperationType, FULLY_CONNECTED);
+CHECK_TEST_ENUM(OperationType, HASHTABLE_LOOKUP);
+CHECK_TEST_ENUM(OperationType, L2_NORMALIZATION);
+CHECK_TEST_ENUM(OperationType, L2_POOL_2D);
+CHECK_TEST_ENUM(OperationType, LOCAL_RESPONSE_NORMALIZATION);
+CHECK_TEST_ENUM(OperationType, LOGISTIC);
+CHECK_TEST_ENUM(OperationType, LSH_PROJECTION);
+CHECK_TEST_ENUM(OperationType, LSTM);
+CHECK_TEST_ENUM(OperationType, MAX_POOL_2D);
+CHECK_TEST_ENUM(OperationType, MUL);
+CHECK_TEST_ENUM(OperationType, RELU);
+CHECK_TEST_ENUM(OperationType, RELU1);
+CHECK_TEST_ENUM(OperationType, RELU6);
+CHECK_TEST_ENUM(OperationType, RESHAPE);
+CHECK_TEST_ENUM(OperationType, RESIZE_BILINEAR);
+CHECK_TEST_ENUM(OperationType, RNN);
+CHECK_TEST_ENUM(OperationType, SOFTMAX);
+CHECK_TEST_ENUM(OperationType, SPACE_TO_DEPTH);
+CHECK_TEST_ENUM(OperationType, SVDF);
+CHECK_TEST_ENUM(OperationType, TANH);
+CHECK_TEST_ENUM(OperationType, BATCH_TO_SPACE_ND);
+CHECK_TEST_ENUM(OperationType, DIV);
+CHECK_TEST_ENUM(OperationType, MEAN);
+CHECK_TEST_ENUM(OperationType, PAD);
+CHECK_TEST_ENUM(OperationType, SPACE_TO_BATCH_ND);
+CHECK_TEST_ENUM(OperationType, SQUEEZE);
+CHECK_TEST_ENUM(OperationType, STRIDED_SLICE);
+CHECK_TEST_ENUM(OperationType, SUB);
+CHECK_TEST_ENUM(OperationType, TRANSPOSE);
+CHECK_TEST_ENUM(OperationType, ABS);
+CHECK_TEST_ENUM(OperationType, ARGMAX);
+CHECK_TEST_ENUM(OperationType, ARGMIN);
+CHECK_TEST_ENUM(OperationType, AXIS_ALIGNED_BBOX_TRANSFORM);
+CHECK_TEST_ENUM(OperationType, BIDIRECTIONAL_SEQUENCE_LSTM);
+CHECK_TEST_ENUM(OperationType, BIDIRECTIONAL_SEQUENCE_RNN);
+CHECK_TEST_ENUM(OperationType, BOX_WITH_NMS_LIMIT);
+CHECK_TEST_ENUM(OperationType, CAST);
+CHECK_TEST_ENUM(OperationType, CHANNEL_SHUFFLE);
+CHECK_TEST_ENUM(OperationType, DETECTION_POSTPROCESSING);
+CHECK_TEST_ENUM(OperationType, EQUAL);
+CHECK_TEST_ENUM(OperationType, EXP);
+CHECK_TEST_ENUM(OperationType, EXPAND_DIMS);
+CHECK_TEST_ENUM(OperationType, GATHER);
+CHECK_TEST_ENUM(OperationType, GENERATE_PROPOSALS);
+CHECK_TEST_ENUM(OperationType, GREATER);
+CHECK_TEST_ENUM(OperationType, GREATER_EQUAL);
+CHECK_TEST_ENUM(OperationType, GROUPED_CONV_2D);
+CHECK_TEST_ENUM(OperationType, HEATMAP_MAX_KEYPOINT);
+CHECK_TEST_ENUM(OperationType, INSTANCE_NORMALIZATION);
+CHECK_TEST_ENUM(OperationType, LESS);
+CHECK_TEST_ENUM(OperationType, LESS_EQUAL);
+CHECK_TEST_ENUM(OperationType, LOG);
+CHECK_TEST_ENUM(OperationType, LOGICAL_AND);
+CHECK_TEST_ENUM(OperationType, LOGICAL_NOT);
+CHECK_TEST_ENUM(OperationType, LOGICAL_OR);
+CHECK_TEST_ENUM(OperationType, LOG_SOFTMAX);
+CHECK_TEST_ENUM(OperationType, MAXIMUM);
+CHECK_TEST_ENUM(OperationType, MINIMUM);
+CHECK_TEST_ENUM(OperationType, NEG);
+CHECK_TEST_ENUM(OperationType, NOT_EQUAL);
+CHECK_TEST_ENUM(OperationType, PAD_V2);
+CHECK_TEST_ENUM(OperationType, POW);
+CHECK_TEST_ENUM(OperationType, PRELU);
+CHECK_TEST_ENUM(OperationType, QUANTIZE);
+CHECK_TEST_ENUM(OperationType, QUANTIZED_16BIT_LSTM);
+CHECK_TEST_ENUM(OperationType, RANDOM_MULTINOMIAL);
+CHECK_TEST_ENUM(OperationType, REDUCE_ALL);
+CHECK_TEST_ENUM(OperationType, REDUCE_ANY);
+CHECK_TEST_ENUM(OperationType, REDUCE_MAX);
+CHECK_TEST_ENUM(OperationType, REDUCE_MIN);
+CHECK_TEST_ENUM(OperationType, REDUCE_PROD);
+CHECK_TEST_ENUM(OperationType, REDUCE_SUM);
+CHECK_TEST_ENUM(OperationType, ROI_ALIGN);
+CHECK_TEST_ENUM(OperationType, ROI_POOLING);
+CHECK_TEST_ENUM(OperationType, RSQRT);
+CHECK_TEST_ENUM(OperationType, SELECT);
+CHECK_TEST_ENUM(OperationType, SIN);
+CHECK_TEST_ENUM(OperationType, SLICE);
+CHECK_TEST_ENUM(OperationType, SPLIT);
+CHECK_TEST_ENUM(OperationType, SQRT);
+CHECK_TEST_ENUM(OperationType, TILE);
+CHECK_TEST_ENUM(OperationType, TOPK_V2);
+CHECK_TEST_ENUM(OperationType, TRANSPOSE_CONV_2D);
+CHECK_TEST_ENUM(OperationType, UNIDIRECTIONAL_SEQUENCE_LSTM);
+CHECK_TEST_ENUM(OperationType, UNIDIRECTIONAL_SEQUENCE_RNN);
+CHECK_TEST_ENUM(OperationType, RESIZE_NEAREST_NEIGHBOR);
+
+#undef CHECK_TEST_ENUM
+
+}  // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/vts/functional/TestMain.cpp b/neuralnetworks/aidl/vts/functional/TestMain.cpp
new file mode 100644
index 0000000..1d58608
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/TestMain.cpp
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+#include <android/binder_process.h>
+#include <gtest/gtest.h>
+#include "LogTestCaseToLogcat.h"
+
+int main(int argc, char** argv) {
+    testing::InitGoogleTest(&argc, argv);
+    testing::UnitTest::GetInstance()->listeners().Append(
+            new aidl::android::hardware::neuralnetworks::LogTestCaseToLogcat());
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/neuralnetworks/aidl/vts/functional/Utils.cpp b/neuralnetworks/aidl/vts/functional/Utils.cpp
new file mode 100644
index 0000000..9af362e
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Utils.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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.
+ */
+
+#include "Utils.h"
+
+#include <aidl/android/hardware/neuralnetworks/IPreparedModelParcel.h>
+#include <aidl/android/hardware/neuralnetworks/Operand.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <android-base/logging.h>
+#include <android/binder_status.h>
+#include <android/hardware_buffer.h>
+
+#include <sys/mman.h>
+#include <iostream>
+#include <limits>
+#include <numeric>
+
+#include <MemoryUtils.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/hal/aidl/Conversions.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+using test_helper::TestBuffer;
+using test_helper::TestModel;
+
+uint32_t sizeOfData(OperandType type) {
+    switch (type) {
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::TENSOR_FLOAT32:
+        case OperandType::TENSOR_INT32:
+            return 4;
+        case OperandType::TENSOR_QUANT16_SYMM:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::FLOAT16:
+        case OperandType::TENSOR_QUANT16_ASYMM:
+            return 2;
+        case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::BOOL:
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
+            return 1;
+        case OperandType::SUBGRAPH:
+            return 0;
+        default:
+            CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+            return 0;
+    }
+}
+
+static bool isTensor(OperandType type) {
+    switch (type) {
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::FLOAT16:
+        case OperandType::BOOL:
+        case OperandType::SUBGRAPH:
+            return false;
+        case OperandType::TENSOR_FLOAT32:
+        case OperandType::TENSOR_INT32:
+        case OperandType::TENSOR_QUANT16_SYMM:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::TENSOR_QUANT16_ASYMM:
+        case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT8_ASYMM_SIGNED:
+            return true;
+        default:
+            CHECK(false) << "Invalid OperandType " << static_cast<uint32_t>(type);
+            return false;
+    }
+}
+
+uint32_t sizeOfData(const Operand& operand) {
+    const uint32_t dataSize = sizeOfData(operand.type);
+    if (isTensor(operand.type) && operand.dimensions.size() == 0) return 0;
+    return std::accumulate(operand.dimensions.begin(), operand.dimensions.end(), dataSize,
+                           std::multiplies<>{});
+}
+
+std::unique_ptr<TestAshmem> TestAshmem::create(uint32_t size, bool aidlReadonly) {
+    auto ashmem = std::make_unique<TestAshmem>(size, aidlReadonly);
+    return ashmem->mIsValid ? std::move(ashmem) : nullptr;
+}
+
+// This function will create a readonly shared memory with PROT_READ only.
+// The input shared memory must be either Ashmem or mapped-FD.
+static nn::SharedMemory convertSharedMemoryToReadonly(const nn::SharedMemory& sharedMemory) {
+    if (std::holds_alternative<nn::Memory::Ashmem>(sharedMemory->handle)) {
+        const auto& memory = std::get<nn::Memory::Ashmem>(sharedMemory->handle);
+        return nn::createSharedMemoryFromFd(memory.size, PROT_READ, memory.fd.get(), /*offset=*/0)
+                .value();
+    } else if (std::holds_alternative<nn::Memory::Fd>(sharedMemory->handle)) {
+        const auto& memory = std::get<nn::Memory::Fd>(sharedMemory->handle);
+        return nn::createSharedMemoryFromFd(memory.size, PROT_READ, memory.fd.get(), memory.offset)
+                .value();
+    }
+    CHECK(false) << "Unexpected shared memory type";
+    return sharedMemory;
+}
+
+void TestAshmem::initialize(uint32_t size, bool aidlReadonly) {
+    mIsValid = false;
+    ASSERT_GT(size, 0);
+    const auto sharedMemory = nn::createSharedMemory(size).value();
+    mMappedMemory = nn::map(sharedMemory).value();
+    mPtr = static_cast<uint8_t*>(std::get<void*>(mMappedMemory.pointer));
+    CHECK_NE(mPtr, nullptr);
+    if (aidlReadonly) {
+        mAidlMemory = utils::convert(convertSharedMemoryToReadonly(sharedMemory)).value();
+    } else {
+        mAidlMemory = utils::convert(sharedMemory).value();
+    }
+    mIsValid = true;
+}
+
+std::unique_ptr<TestBlobAHWB> TestBlobAHWB::create(uint32_t size) {
+    auto ahwb = std::make_unique<TestBlobAHWB>(size);
+    return ahwb->mIsValid ? std::move(ahwb) : nullptr;
+}
+
+void TestBlobAHWB::initialize(uint32_t size) {
+    mIsValid = false;
+    ASSERT_GT(size, 0);
+    const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+    const AHardwareBuffer_Desc desc = {
+            .width = size,
+            .height = 1,
+            .layers = 1,
+            .format = AHARDWAREBUFFER_FORMAT_BLOB,
+            .usage = usage,
+            .stride = size,
+    };
+
+    ASSERT_EQ(AHardwareBuffer_allocate(&desc, &mAhwb), 0);
+    ASSERT_NE(mAhwb, nullptr);
+
+    const auto sharedMemory =
+            nn::createSharedMemoryFromAHWB(mAhwb, /*takeOwnership=*/false).value();
+    mMapping = nn::map(sharedMemory).value();
+    mPtr = static_cast<uint8_t*>(std::get<void*>(mMapping.pointer));
+    CHECK_NE(mPtr, nullptr);
+    mAidlMemory = utils::convert(sharedMemory).value();
+
+    mIsValid = true;
+}
+
+TestBlobAHWB::~TestBlobAHWB() {
+    if (mAhwb) {
+        AHardwareBuffer_unlock(mAhwb, nullptr);
+        AHardwareBuffer_release(mAhwb);
+    }
+}
+
+std::string gtestCompliantName(std::string name) {
+    // gtest test names must only contain alphanumeric characters
+    std::replace_if(
+            name.begin(), name.end(), [](char c) { return !std::isalnum(c); }, '_');
+    return name;
+}
+
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus) {
+    return os << toString(errorStatus);
+}
+
+Request ExecutionContext::createRequest(const TestModel& testModel, MemoryType memoryType) {
+    CHECK(memoryType == MemoryType::ASHMEM || memoryType == MemoryType::BLOB_AHWB);
+
+    // Model inputs.
+    std::vector<RequestArgument> inputs(testModel.main.inputIndexes.size());
+    size_t inputSize = 0;
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
+        if (op.data.size() == 0) {
+            // Omitted input.
+            inputs[i] = {.hasNoValue = true};
+        } else {
+            DataLocation loc = {.poolIndex = kInputPoolIndex,
+                                .offset = static_cast<int64_t>(inputSize),
+                                .length = static_cast<int64_t>(op.data.size())};
+            inputSize += op.data.alignedSize();
+            inputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+        }
+    }
+
+    // Model outputs.
+    std::vector<RequestArgument> outputs(testModel.main.outputIndexes.size());
+    size_t outputSize = 0;
+    for (uint32_t i = 0; i < testModel.main.outputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.outputIndexes[i]];
+
+        // In the case of zero-sized output, we should at least provide a one-byte buffer.
+        // This is because zero-sized tensors are only supported internally to the driver, or
+        // reported in output shapes. It is illegal for the client to pre-specify a zero-sized
+        // tensor as model output. Otherwise, we will have two semantic conflicts:
+        // - "Zero dimension" conflicts with "unspecified dimension".
+        // - "Omitted operand buffer" conflicts with "zero-sized operand buffer".
+        size_t bufferSize = std::max<size_t>(op.data.size(), 1);
+
+        DataLocation loc = {.poolIndex = kOutputPoolIndex,
+                            .offset = static_cast<int64_t>(outputSize),
+                            .length = static_cast<int64_t>(bufferSize)};
+        outputSize += op.data.size() == 0 ? TestBuffer::kAlignment : op.data.alignedSize();
+        outputs[i] = {.hasNoValue = false, .location = loc, .dimensions = {}};
+    }
+
+    // Allocate memory pools.
+    if (memoryType == MemoryType::ASHMEM) {
+        mInputMemory = TestAshmem::create(inputSize);
+        mOutputMemory = TestAshmem::create(outputSize);
+    } else {
+        mInputMemory = TestBlobAHWB::create(inputSize);
+        mOutputMemory = TestBlobAHWB::create(outputSize);
+    }
+    CHECK_NE(mInputMemory, nullptr);
+    CHECK_NE(mOutputMemory, nullptr);
+
+    auto copiedInputMemory = utils::clone(*mInputMemory->getAidlMemory());
+    CHECK(copiedInputMemory.has_value()) << copiedInputMemory.error().message;
+    auto copiedOutputMemory = utils::clone(*mOutputMemory->getAidlMemory());
+    CHECK(copiedOutputMemory.has_value()) << copiedOutputMemory.error().message;
+
+    std::vector<RequestMemoryPool> pools;
+    pools.push_back(RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+            std::move(copiedInputMemory).value()));
+    pools.push_back(RequestMemoryPool::make<RequestMemoryPool::Tag::pool>(
+            std::move(copiedOutputMemory).value()));
+
+    // Copy input data to the memory pool.
+    uint8_t* inputPtr = mInputMemory->getPointer();
+    for (uint32_t i = 0; i < testModel.main.inputIndexes.size(); i++) {
+        const auto& op = testModel.main.operands[testModel.main.inputIndexes[i]];
+        if (op.data.size() > 0) {
+            const uint8_t* begin = op.data.get<uint8_t>();
+            const uint8_t* end = begin + op.data.size();
+            std::copy(begin, end, inputPtr + inputs[i].location.offset);
+        }
+    }
+
+    return {.inputs = std::move(inputs), .outputs = std::move(outputs), .pools = std::move(pools)};
+}
+
+std::vector<TestBuffer> ExecutionContext::getOutputBuffers(const Request& request) const {
+    // Copy out output results.
+    uint8_t* outputPtr = mOutputMemory->getPointer();
+    std::vector<TestBuffer> outputBuffers;
+    for (const auto& output : request.outputs) {
+        outputBuffers.emplace_back(output.location.length, outputPtr + output.location.offset);
+    }
+    return outputBuffers;
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks
diff --git a/neuralnetworks/aidl/vts/functional/Utils.h b/neuralnetworks/aidl/vts/functional/Utils.h
new file mode 100644
index 0000000..9dd7359
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/Utils.h
@@ -0,0 +1,156 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H
+
+#include <android-base/logging.h>
+#include <android/hardware_buffer.h>
+#include <gtest/gtest.h>
+
+#include <algorithm>
+#include <iosfwd>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <aidl/android/hardware/neuralnetworks/Memory.h>
+#include <aidl/android/hardware/neuralnetworks/Operand.h>
+#include <aidl/android/hardware/neuralnetworks/OperandType.h>
+#include <aidl/android/hardware/neuralnetworks/Priority.h>
+#include <aidl/android/hardware/neuralnetworks/Request.h>
+
+#include <TestHarness.h>
+#include <nnapi/SharedMemory.h>
+
+namespace aidl::android::hardware::neuralnetworks {
+
+namespace nn = ::android::nn;
+
+inline constexpr Priority kDefaultPriority = Priority::MEDIUM;
+
+inline constexpr Timing kNoTiming = {.timeOnDeviceNs = -1, .timeInDriverNs = -1};
+inline constexpr int64_t kNoDeadline = -1;
+inline constexpr int64_t kOmittedTimeoutDuration = -1;
+inline constexpr int64_t kNoDuration = -1;
+inline const std::vector<uint8_t> kEmptyCacheToken(IDevice::BYTE_SIZE_OF_CACHE_TOKEN);
+
+// Returns the amount of space needed to store a value of the specified type.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(OperandType type);
+
+// Returns the amount of space needed to store a value of the dimensions and
+// type of this operand. For a non-extension, non-OEM tensor with unspecified
+// rank or at least one unspecified dimension, returns zero.
+//
+// Aborts if the specified type is an extension type or OEM type.
+uint32_t sizeOfData(const Operand& operand);
+
+// Convenience class to manage the lifetime of memory resources.
+class TestMemoryBase {
+    DISALLOW_COPY_AND_ASSIGN(TestMemoryBase);
+
+  public:
+    TestMemoryBase() = default;
+    virtual ~TestMemoryBase() = default;
+    uint8_t* getPointer() const { return mPtr; }
+    const Memory* getAidlMemory() const { return &mAidlMemory; }
+
+  protected:
+    uint8_t* mPtr = nullptr;
+    Memory mAidlMemory;
+    bool mIsValid = false;
+};
+
+class TestAshmem : public TestMemoryBase {
+  public:
+    // If aidlReadonly is true, getAidlMemory will return a sAIDL memory with readonly access;
+    // otherwise, the sAIDL memory has read-write access. This only affects the sAIDL memory.
+    // getPointer will always return a valid address with read-write access.
+    static std::unique_ptr<TestAshmem> create(uint32_t size, bool aidlReadonly = false);
+
+    // Prefer TestAshmem::create.
+    // The constructor calls initialize, which constructs the memory resources. This is a workaround
+    // that gtest macros cannot be used directly in a constructor.
+    TestAshmem(uint32_t size, bool aidlReadonly) { initialize(size, aidlReadonly); }
+
+  private:
+    void initialize(uint32_t size, bool aidlReadonly);
+    nn::Mapping mMappedMemory;
+};
+
+class TestBlobAHWB : public TestMemoryBase {
+  public:
+    static std::unique_ptr<TestBlobAHWB> create(uint32_t size);
+
+    // Prefer TestBlobAHWB::create.
+    // The constructor calls initialize, which constructs the memory resources. This is a
+    // workaround that gtest macros cannot be used directly in a constructor.
+    TestBlobAHWB(uint32_t size) { initialize(size); }
+    ~TestBlobAHWB();
+
+  private:
+    void initialize(uint32_t size);
+    AHardwareBuffer* mAhwb = nullptr;
+    nn::Mapping mMapping;
+};
+
+enum class MemoryType { ASHMEM, BLOB_AHWB, DEVICE };
+
+// Manages the lifetime of memory resources used in an execution.
+class ExecutionContext {
+    DISALLOW_COPY_AND_ASSIGN(ExecutionContext);
+
+  public:
+    static constexpr uint32_t kInputPoolIndex = 0;
+    static constexpr uint32_t kOutputPoolIndex = 1;
+
+    ExecutionContext() = default;
+
+    // Create HIDL Request from the TestModel struct.
+    Request createRequest(const test_helper::TestModel& testModel,
+                          MemoryType memoryType = MemoryType::ASHMEM);
+
+    // After execution, copy out output results from the output memory pool.
+    std::vector<test_helper::TestBuffer> getOutputBuffers(const Request& request) const;
+
+  private:
+    std::unique_ptr<TestMemoryBase> mInputMemory, mOutputMemory;
+};
+
+template <typename Type>
+using Named = std::pair<std::string, Type>;
+
+template <typename Type>
+const std::string& getName(const Named<Type>& namedData) {
+    return namedData.first;
+}
+
+template <typename Type>
+const Type& getData(const Named<Type>& namedData) {
+    return namedData.second;
+}
+
+std::string gtestCompliantName(std::string name);
+
+// pretty-print values for error messages
+::std::ostream& operator<<(::std::ostream& os, ErrorStatus errorStatus);
+
+}  // namespace aidl::android::hardware::neuralnetworks
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_AIDL_UTILS_H
diff --git a/neuralnetworks/aidl/vts/functional/ValidateModel.cpp b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
new file mode 100644
index 0000000..698c054
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/ValidateModel.cpp
@@ -0,0 +1,1344 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
+#include <aidl/android/hardware/common/NativeHandle.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_enums.h>
+#include <android/binder_interface_utils.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/hal/aidl/Conversions.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+#include <optional>
+#include <type_traits>
+#include <utility>
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using common::NativeHandle;
+using implementation::PreparedModelCallback;
+
+using PrepareModelMutation = std::function<void(Model*, ExecutionPreference*, Priority*)>;
+
+///////////////////////// UTILITY FUNCTIONS /////////////////////////
+
+static void validateGetSupportedOperations(const std::shared_ptr<IDevice>& device,
+                                           const std::string& message, const Model& model) {
+    SCOPED_TRACE(message + " [getSupportedOperations]");
+
+    std::vector<bool> supported;
+    const auto retStatus = device->getSupportedOperations(model, &supported);
+
+    ASSERT_FALSE(retStatus.isOk());
+    ASSERT_EQ(retStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    ASSERT_EQ(static_cast<ErrorStatus>(retStatus.getServiceSpecificError()),
+              ErrorStatus::INVALID_ARGUMENT);
+}
+
+static void validatePrepareModel(const std::shared_ptr<IDevice>& device, const std::string& message,
+                                 const Model& model, ExecutionPreference preference,
+                                 Priority priority) {
+    SCOPED_TRACE(message + " [prepareModel]");
+
+    std::shared_ptr<PreparedModelCallback> preparedModelCallback =
+            ndk::SharedRefBase::make<PreparedModelCallback>();
+    const auto prepareLaunchStatus =
+            device->prepareModel(model, preference, priority, kNoDeadline, {}, {}, kEmptyCacheToken,
+                                 preparedModelCallback);
+    ASSERT_FALSE(prepareLaunchStatus.isOk());
+    ASSERT_EQ(prepareLaunchStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    ASSERT_EQ(static_cast<ErrorStatus>(prepareLaunchStatus.getServiceSpecificError()),
+              ErrorStatus::INVALID_ARGUMENT);
+
+    preparedModelCallback->wait();
+    ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+    ASSERT_EQ(ErrorStatus::INVALID_ARGUMENT, prepareReturnStatus);
+    std::shared_ptr<IPreparedModel> preparedModel = preparedModelCallback->getPreparedModel();
+    ASSERT_EQ(nullptr, preparedModel.get());
+}
+
+static bool validExecutionPreference(ExecutionPreference preference) {
+    return preference == ExecutionPreference::LOW_POWER ||
+           preference == ExecutionPreference::FAST_SINGLE_ANSWER ||
+           preference == ExecutionPreference::SUSTAINED_SPEED;
+}
+
+static bool validExecutionPriority(Priority priority) {
+    return priority == Priority::LOW || priority == Priority::MEDIUM || priority == Priority::HIGH;
+}
+
+// Primary validation function. This function will take a valid model, apply a
+// mutation to invalidate the model, the execution preference, or the priority,
+// then pass these to supportedOperations and/or prepareModel if that method is
+// called with an invalid argument.
+static void validate(const std::shared_ptr<IDevice>& device, const std::string& message,
+                     const Model& originalModel, const PrepareModelMutation& mutate) {
+    Model model = utils::clone(originalModel).value();
+    ExecutionPreference preference = ExecutionPreference::FAST_SINGLE_ANSWER;
+    Priority priority = kDefaultPriority;
+    mutate(&model, &preference, &priority);
+
+    if (validExecutionPreference(preference) && validExecutionPriority(priority)) {
+        validateGetSupportedOperations(device, message, model);
+    }
+
+    validatePrepareModel(device, message, model, preference, priority);
+}
+
+static uint32_t addOperand(Model* model) {
+    model->main.operands.push_back({
+            .type = OperandType::INT32,
+            .dimensions = {},
+            .scale = 0.0f,
+            .zeroPoint = 0,
+            .lifetime = OperandLifeTime::SUBGRAPH_INPUT,
+            .location = {.poolIndex = 0, .offset = 0, .length = 0},
+    });
+    return model->main.operands.size() - 1;
+}
+
+static uint32_t addOperand(Model* model, OperandLifeTime lifetime) {
+    uint32_t index = addOperand(model);
+    model->main.operands[index].lifetime = lifetime;
+    return index;
+}
+
+// If we introduce a CONSTANT_COPY for an operand of size operandSize,
+// how much will this increase the size of the model?  This assumes
+// that we can (re)use all of model.operandValues for the operand
+// value.
+static size_t constantCopyExtraSize(const Model& model, size_t operandSize) {
+    const size_t operandValuesSize = model.operandValues.size();
+    return (operandValuesSize < operandSize) ? (operandSize - operandValuesSize) : 0;
+}
+
+// Highly specialized utility routine for converting an operand to
+// CONSTANT_COPY lifetime.
+//
+// Expects that:
+// - operand has a known size
+// - operand->lifetime has already been set to CONSTANT_COPY
+// - operand->location has been zeroed out
+//
+// Does the following:
+// - initializes operand->location to point to the beginning of model->operandValues
+// - resizes model->operandValues (if necessary) to be large enough for the operand
+//   value, padding it with zeroes on the end
+//
+// Potential problem:
+// By changing the operand to CONSTANT_COPY lifetime, this function is effectively initializing the
+// operand with unspecified (but deterministic) data. This means that the model may be invalidated
+// in two ways: not only is the lifetime of CONSTANT_COPY invalid, but the operand's value in the
+// graph may also be invalid (e.g., if the operand is used as an activation code and has an invalid
+// value). For now, this should be fine because it just means we're not testing what we think we're
+// testing in certain cases; but we can handwave this and assume we're probabilistically likely to
+// exercise the validation code over the span of the entire test set and operand space.
+//
+// Aborts if the specified operand type is an extension type or OEM type.
+static void becomeConstantCopy(Model* model, Operand* operand) {
+    // sizeOfData will abort if the specified type is an extension type or OEM type.
+    const size_t sizeOfOperand = sizeOfData(*operand);
+    EXPECT_NE(sizeOfOperand, size_t(0));
+    operand->location.poolIndex = 0;
+    operand->location.offset = 0;
+    operand->location.length = sizeOfOperand;
+    if (model->operandValues.size() < sizeOfOperand) {
+        model->operandValues.resize(sizeOfOperand);
+    }
+}
+
+// The sizeForBinder() functions estimate the size of the
+// representation of a value when sent to binder.  It's probably a bit
+// of an under-estimate, because we don't know the size of the
+// metadata in the binder format (e.g., representation of the size of
+// a vector); but at least it adds up "big" things like vector
+// contents.  However, it doesn't treat inter-field or end-of-struct
+// padding in a methodical way -- there's no attempt to be consistent
+// in whether or not padding in the native (C++) representation
+// contributes to the estimated size for the binder representation;
+// and there's no attempt to understand what padding (if any) is
+// needed in the binder representation.
+//
+// This assumes that non-metadata uses a fixed length encoding (e.g.,
+// a uint32_t is always encoded in sizeof(uint32_t) bytes, rather than
+// using an encoding whose length is related to the magnitude of the
+// encoded value).
+
+template <typename Type>
+static size_t sizeForBinder(const Type& val) {
+    static_assert(std::is_trivially_copyable_v<std::remove_reference_t<Type>>,
+                  "expected a trivially copyable type");
+    return sizeof(val);
+}
+
+template <typename Type>
+static size_t sizeForBinder(const std::vector<Type>& vec) {
+    return std::accumulate(vec.begin(), vec.end(), 0,
+                           [](size_t acc, const Type& x) { return acc + sizeForBinder(x); });
+}
+
+template <>
+size_t sizeForBinder(const SymmPerChannelQuantParams& symmPerChannelQuantParams) {
+    size_t size = 0;
+
+    size += sizeForBinder(symmPerChannelQuantParams.scales);
+    size += sizeForBinder(symmPerChannelQuantParams.channelDim);
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const std::optional<OperandExtraParams>& optionalExtraParams) {
+    if (!optionalExtraParams.has_value()) {
+        return 0;
+    }
+    const auto& extraParams = optionalExtraParams.value();
+    using Tag = OperandExtraParams::Tag;
+    switch (extraParams.getTag()) {
+        case Tag::channelQuant:
+            return sizeForBinder(extraParams.get<Tag::channelQuant>());
+        case Tag::extension:
+            return sizeForBinder(extraParams.get<Tag::extension>());
+    }
+    LOG(FATAL) << "Unrecognized extraParams tag: " << static_cast<int>(extraParams.getTag());
+    return 0;
+}
+
+template <>
+size_t sizeForBinder(const Operand& operand) {
+    size_t size = 0;
+
+    size += sizeForBinder(operand.type);
+    size += sizeForBinder(operand.dimensions);
+    size += sizeForBinder(operand.scale);
+    size += sizeForBinder(operand.zeroPoint);
+    size += sizeForBinder(operand.lifetime);
+    size += sizeForBinder(operand.location);
+    size += sizeForBinder(operand.extraParams);
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const Operation& operation) {
+    size_t size = 0;
+
+    size += sizeForBinder(operation.type);
+    size += sizeForBinder(operation.inputs);
+    size += sizeForBinder(operation.outputs);
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const std::string& name) {
+    return name.size();
+}
+
+template <>
+size_t sizeForBinder(const Memory& memory) {
+    // This is just a guess.
+
+    size_t size = sizeof(Memory);
+
+    // Only hardwareBuffer type memory has dynamic memory that needs to be accounted for (in the
+    // form of a NativeHandle type). The other other types of memory (MappableFile, Ashmem) use a
+    // single file descriptor (with metadata) instead.
+    if (memory.getTag() == Memory::Tag::hardwareBuffer) {
+        const NativeHandle& handle = memory.get<Memory::Tag::hardwareBuffer>().handle;
+        size += sizeof(decltype(handle.fds)::value_type) * handle.fds.size();
+        size += sizeof(decltype(handle.ints)::value_type) * handle.ints.size();
+    }
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const Subgraph& subgraph) {
+    size_t size = 0;
+
+    size += sizeForBinder(subgraph.operands);
+    size += sizeForBinder(subgraph.operations);
+    size += sizeForBinder(subgraph.inputIndexes);
+    size += sizeForBinder(subgraph.outputIndexes);
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const ExtensionNameAndPrefix& extensionNameToPrefix) {
+    size_t size = 0;
+
+    size += sizeForBinder(extensionNameToPrefix.name);
+    size += sizeForBinder(extensionNameToPrefix.prefix);
+
+    return size;
+}
+
+template <>
+size_t sizeForBinder(const Model& model) {
+    size_t size = 0;
+
+    size += sizeForBinder(model.main);
+    size += sizeForBinder(model.referenced);
+    size += sizeForBinder(model.operandValues);
+    size += sizeForBinder(model.pools);
+    size += sizeForBinder(model.relaxComputationFloat32toFloat16);
+    size += sizeForBinder(model.extensionNameToPrefix);
+
+    return size;
+}
+
+// https://developer.android.com/reference/android/os/TransactionTooLargeException.html
+//
+//     "The Binder transaction buffer has a limited fixed size,
+//     currently 1Mb, which is shared by all transactions in progress
+//     for the process."
+//
+// Will our representation fit under this limit?  There are two complications:
+// - Our representation size is just approximate (see sizeForBinder()).
+// - This object may not be the only occupant of the Binder transaction buffer.
+// So we'll be very conservative: We want the representation size to be no
+// larger than half the transaction buffer size.
+//
+// If our representation grows large enough that it still fits within
+// the transaction buffer but combined with other transactions may
+// exceed the buffer size, then we may see intermittent HAL transport
+// errors.
+static bool exceedsBinderSizeLimit(size_t representationSize) {
+    // Instead of using this fixed buffer size, we might instead be able to use
+    // ProcessState::self()->getMmapSize(). However, this has a potential
+    // problem: The binder/mmap size of the current process does not necessarily
+    // indicate the binder/mmap size of the service (i.e., the other process).
+    // The only way it would be a good indication is if both the current process
+    // and the service use the default size.
+    static const size_t kHalfBufferSize = 1024 * 1024 / 2;
+
+    return representationSize > kHalfBufferSize;
+}
+
+///////////////////////// VALIDATE EXECUTION ORDER ////////////////////////////
+
+static void mutateExecutionOrderTest(const std::shared_ptr<IDevice>& device, const Model& model,
+                                     const std::vector<uint32_t>& numberOfConsumers) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        const Operation& operationObj = model.main.operations[operation];
+        for (uint32_t input : operationObj.inputs) {
+            if (model.main.operands[input].lifetime == OperandLifeTime::TEMPORARY_VARIABLE ||
+                model.main.operands[input].lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+                // This operation reads an operand written by some
+                // other operation.  Move this operation to the
+                // beginning of the sequence, ensuring that it reads
+                // the operand before that operand is written, thereby
+                // violating execution order rules.
+                const std::string message = "mutateExecutionOrderTest: operation " +
+                                            std::to_string(operation) + " is a reader";
+                validate(device, message, model,
+                         [operation](Model* model, ExecutionPreference*, Priority*) {
+                             auto& operations = model->main.operations;
+                             std::rotate(operations.begin(), operations.begin() + operation,
+                                         operations.begin() + operation + 1);
+                         });
+                break;  // only need to do this once per operation
+            }
+        }
+        for (uint32_t output : operationObj.outputs) {
+            if (numberOfConsumers[output] > 0) {
+                // This operation writes an operand read by some other
+                // operation.  Move this operation to the end of the
+                // sequence, ensuring that it writes the operand after
+                // that operand is read, thereby violating execution
+                // order rules.
+                const std::string message = "mutateExecutionOrderTest: operation " +
+                                            std::to_string(operation) + " is a writer";
+                validate(device, message, model,
+                         [operation](Model* model, ExecutionPreference*, Priority*) {
+                             auto& operations = model->main.operations;
+                             std::rotate(operations.begin() + operation,
+                                         operations.begin() + operation + 1, operations.end());
+                         });
+                break;  // only need to do this once per operation
+            }
+        }
+    }
+}
+
+///////////////////////// VALIDATE MODEL OPERAND TYPE /////////////////////////
+
+static const int32_t invalidOperandTypes[] = {
+        -1,
+        static_cast<int32_t>(*(ndk::enum_range<OperandType>().end() - 1)) + 1,
+};
+
+static void mutateOperandTypeTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        for (int32_t invalidOperandType : invalidOperandTypes) {
+            const std::string message = "mutateOperandTypeTest: operand " +
+                                        std::to_string(operand) + " set to value " +
+                                        std::to_string(invalidOperandType);
+            validate(device, message, model,
+                     [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
+                         model->main.operands[operand].type =
+                                 static_cast<OperandType>(invalidOperandType);
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE OPERAND RANK /////////////////////////
+
+static uint32_t getInvalidRank(OperandType type) {
+    switch (type) {
+        case OperandType::FLOAT16:
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::BOOL:
+            return 1;
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::TENSOR_FLOAT32:
+        case OperandType::TENSOR_INT32:
+        case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
+        case OperandType::TENSOR_QUANT16_SYMM:
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+            return 0;
+        default:
+            return 0;
+    }
+}
+
+static void mutateOperandRankTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        const uint32_t invalidRank = getInvalidRank(model.main.operands[operand].type);
+        if (invalidRank == 0) {
+            continue;
+        }
+        const std::string message = "mutateOperandRankTest: operand " + std::to_string(operand) +
+                                    " has rank of " + std::to_string(invalidRank);
+        validate(device, message, model,
+                 [operand, invalidRank](Model* model, ExecutionPreference*, Priority*) {
+                     model->main.operands[operand].dimensions =
+                             std::vector<int32_t>(invalidRank, 0);
+                 });
+    }
+}
+
+///////////////////////// VALIDATE OPERAND SCALE /////////////////////////
+
+static float getInvalidScale(OperandType type) {
+    switch (type) {
+        case OperandType::FLOAT16:
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::BOOL:
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::TENSOR_FLOAT32:
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::SUBGRAPH:
+            return 1.0f;
+        case OperandType::TENSOR_INT32:
+            return -1.0f;
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
+        case OperandType::TENSOR_QUANT16_SYMM:
+            return 0.0f;
+        default:
+            return 0.0f;
+    }
+}
+
+static void mutateOperandScaleTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        const float invalidScale = getInvalidScale(model.main.operands[operand].type);
+        const std::string message = "mutateOperandScaleTest: operand " + std::to_string(operand) +
+                                    " has scale of " + std::to_string(invalidScale);
+        validate(device, message, model,
+                 [operand, invalidScale](Model* model, ExecutionPreference*, Priority*) {
+                     model->main.operands[operand].scale = invalidScale;
+                 });
+    }
+}
+
+///////////////////////// VALIDATE OPERAND ZERO POINT /////////////////////////
+
+static std::vector<int32_t> getInvalidZeroPoints(OperandType type) {
+    switch (type) {
+        case OperandType::FLOAT16:
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::BOOL:
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::TENSOR_FLOAT32:
+        case OperandType::TENSOR_INT32:
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL:
+        case OperandType::SUBGRAPH:
+            return {1};
+        case OperandType::TENSOR_QUANT8_ASYMM:
+            return {-1, 256};
+        case OperandType::TENSOR_QUANT8_SYMM:
+            return {-129, -1, 1, 128};
+        case OperandType::TENSOR_QUANT16_ASYMM:
+            return {-1, 65536};
+        case OperandType::TENSOR_QUANT16_SYMM:
+            return {-32769, -1, 1, 32768};
+        default:
+            return {};
+    }
+}
+
+static void mutateOperandZeroPointTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        const std::vector<int32_t> invalidZeroPoints =
+                getInvalidZeroPoints(model.main.operands[operand].type);
+        for (int32_t invalidZeroPoint : invalidZeroPoints) {
+            const std::string message = "mutateOperandZeroPointTest: operand " +
+                                        std::to_string(operand) + " has zero point of " +
+                                        std::to_string(invalidZeroPoint);
+            validate(device, message, model,
+                     [operand, invalidZeroPoint](Model* model, ExecutionPreference*, Priority*) {
+                         model->main.operands[operand].zeroPoint = invalidZeroPoint;
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE OPERAND LIFETIME /////////////////////////////////////////////
+
+static std::vector<OperandLifeTime> getInvalidLifeTimes(const Model& model, size_t modelSize,
+                                                        const Operand& operand) {
+    // TODO: Support OperandLifeTime::CONSTANT_REFERENCE as an invalid lifetime
+    // TODO: Support OperandLifeTime::NO_VALUE as an invalid lifetime
+
+    // Ways to get an invalid lifetime:
+    // - change whether a lifetime means an operand should have a writer
+    std::vector<OperandLifeTime> ret;
+    switch (operand.lifetime) {
+        case OperandLifeTime::SUBGRAPH_OUTPUT:
+        case OperandLifeTime::TEMPORARY_VARIABLE:
+            ret = {
+                    OperandLifeTime::SUBGRAPH_INPUT,
+                    OperandLifeTime::CONSTANT_COPY,
+            };
+            break;
+        case OperandLifeTime::CONSTANT_COPY:
+        case OperandLifeTime::CONSTANT_POOL:
+        case OperandLifeTime::SUBGRAPH_INPUT:
+            ret = {
+                    OperandLifeTime::TEMPORARY_VARIABLE,
+                    OperandLifeTime::SUBGRAPH_OUTPUT,
+            };
+            break;
+        case OperandLifeTime::NO_VALUE:
+            // Not enough information to know whether
+            // TEMPORARY_VARIABLE or CONSTANT_COPY would be invalid --
+            // is this operand written (then CONSTANT_COPY would be
+            // invalid) or not (then TEMPORARY_VARIABLE would be
+            // invalid)?
+            break;
+        case OperandLifeTime::SUBGRAPH:
+            break;
+        default:
+            ADD_FAILURE();
+            break;
+    }
+
+    const size_t operandSize = sizeOfData(operand);  // will be zero if shape is unknown
+    if (!operandSize ||
+        exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+        // Unknown size or too-large size
+        ret.erase(std::remove(ret.begin(), ret.end(), OperandLifeTime::CONSTANT_COPY), ret.end());
+    }
+
+    return ret;
+}
+
+static void mutateOperandLifeTimeTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    const size_t modelSize = sizeForBinder(model);
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        const std::vector<OperandLifeTime> invalidLifeTimes =
+                getInvalidLifeTimes(model, modelSize, model.main.operands[operand]);
+        for (OperandLifeTime invalidLifeTime : invalidLifeTimes) {
+            const std::string message = "mutateOperandLifetimeTest: operand " +
+                                        std::to_string(operand) + " has lifetime " +
+                                        toString(invalidLifeTime) + " instead of lifetime " +
+                                        toString(model.main.operands[operand].lifetime);
+            validate(device, message, model,
+                     [operand, invalidLifeTime](Model* model, ExecutionPreference*, Priority*) {
+                         static const DataLocation kZeroDataLocation = {};
+                         Operand& operandObj = model->main.operands[operand];
+                         switch (operandObj.lifetime) {
+                             case OperandLifeTime::SUBGRAPH_INPUT: {
+                                 auto& inputs = model->main.inputIndexes;
+                                 inputs.erase(std::remove(inputs.begin(), inputs.end(), operand),
+                                              inputs.end());
+                                 break;
+                             }
+                             case OperandLifeTime::SUBGRAPH_OUTPUT: {
+                                 auto& outputs = model->main.outputIndexes;
+                                 outputs.erase(std::remove(outputs.begin(), outputs.end(), operand),
+                                               outputs.end());
+                                 break;
+                             }
+                             default:
+                                 break;
+                         }
+                         operandObj.lifetime = invalidLifeTime;
+                         operandObj.location = kZeroDataLocation;
+                         switch (invalidLifeTime) {
+                             case OperandLifeTime::CONSTANT_COPY: {
+                                 becomeConstantCopy(model, &operandObj);
+                                 break;
+                             }
+                             case OperandLifeTime::SUBGRAPH_INPUT:
+                                 model->main.inputIndexes.push_back(operand);
+                                 break;
+                             case OperandLifeTime::SUBGRAPH_OUTPUT:
+                                 model->main.outputIndexes.push_back(operand);
+                                 break;
+                             default:
+                                 break;
+                         }
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE OPERAND INPUT-or-OUTPUT //////////////////////////////////////
+
+static std::optional<OperandLifeTime> getInputOutputLifeTime(const Model& model, size_t modelSize,
+                                                             const Operand& operand) {
+    // Ways to get an invalid lifetime (with respect to model inputIndexes and outputIndexes):
+    // - change whether a lifetime means an operand is a model input, a model output, or neither
+    // - preserve whether or not a lifetime means an operand should have a writer
+    switch (operand.lifetime) {
+        case OperandLifeTime::CONSTANT_COPY:
+        case OperandLifeTime::CONSTANT_POOL:
+            return OperandLifeTime::SUBGRAPH_INPUT;
+        case OperandLifeTime::SUBGRAPH_INPUT: {
+            const size_t operandSize = sizeOfData(operand);  // will be zero if shape is unknown
+            if (!operandSize ||
+                exceedsBinderSizeLimit(modelSize + constantCopyExtraSize(model, operandSize))) {
+                // Unknown size or too-large size
+                break;
+            }
+            return OperandLifeTime::CONSTANT_COPY;
+        }
+        case OperandLifeTime::SUBGRAPH_OUTPUT:
+            return OperandLifeTime::TEMPORARY_VARIABLE;
+        case OperandLifeTime::TEMPORARY_VARIABLE:
+            return OperandLifeTime::SUBGRAPH_OUTPUT;
+        case OperandLifeTime::NO_VALUE:
+            // Not enough information to know whether
+            // TEMPORARY_VARIABLE or CONSTANT_COPY would be an
+            // appropriate choice -- is this operand written (then
+            // TEMPORARY_VARIABLE would be appropriate) or not (then
+            // CONSTANT_COPY would be appropriate)?
+            break;
+        case OperandLifeTime::SUBGRAPH:
+            break;
+        default:
+            ADD_FAILURE();
+            break;
+    }
+
+    return std::nullopt;
+}
+
+static void mutateOperandInputOutputTest(const std::shared_ptr<IDevice>& device,
+                                         const Model& model) {
+    const size_t modelSize = sizeForBinder(model);
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        const std::optional<OperandLifeTime> changedLifeTime =
+                getInputOutputLifeTime(model, modelSize, model.main.operands[operand]);
+        if (changedLifeTime) {
+            const std::string message = "mutateOperandInputOutputTest: operand " +
+                                        std::to_string(operand) + " has lifetime " +
+                                        toString(*changedLifeTime) + " instead of lifetime " +
+                                        toString(model.main.operands[operand].lifetime);
+            validate(device, message, model,
+                     [operand, changedLifeTime](Model* model, ExecutionPreference*, Priority*) {
+                         static const DataLocation kZeroDataLocation = {};
+                         Operand& operandObj = model->main.operands[operand];
+                         operandObj.lifetime = *changedLifeTime;
+                         operandObj.location = kZeroDataLocation;
+                         if (*changedLifeTime == OperandLifeTime::CONSTANT_COPY) {
+                             becomeConstantCopy(model, &operandObj);
+                         }
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE OPERAND NUMBER OF WRITERS ////////////////////////////////////
+
+static void mutateOperandAddWriterTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        for (size_t badOutputNum = 0;
+             badOutputNum < model.main.operations[operation].outputs.size(); ++badOutputNum) {
+            const uint32_t outputOperandIndex =
+                    model.main.operations[operation].outputs[badOutputNum];
+            const std::string message = "mutateOperandAddWriterTest: operation " +
+                                        std::to_string(operation) + " writes to " +
+                                        std::to_string(outputOperandIndex);
+            // We'll insert a copy of the operation, all of whose
+            // OTHER output operands are newly-created -- i.e.,
+            // there'll only be a duplicate write of ONE of that
+            // operation's output operands.
+            validate(device, message, model,
+                     [operation, badOutputNum](Model* model, ExecutionPreference*, Priority*) {
+                         Operation newOperation = model->main.operations[operation];
+                         for (size_t outputNum = 0; outputNum < newOperation.outputs.size();
+                              ++outputNum) {
+                             if (outputNum == badOutputNum) continue;
+
+                             Operand operandValue =
+                                     model->main.operands[newOperation.outputs[outputNum]];
+                             if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+                                 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+                             } else {
+                                 ASSERT_EQ(operandValue.lifetime,
+                                           OperandLifeTime::TEMPORARY_VARIABLE);
+                             }
+                             newOperation.outputs[outputNum] = model->main.operands.size();
+                             model->main.operands.push_back(operandValue);
+                         }
+                         // Where do we insert the extra writer (a new
+                         // operation)?  It has to be later than all the
+                         // writers of its inputs.  The easiest thing to do
+                         // is to insert it at the end of the operation
+                         // sequence.
+                         model->main.operations.push_back(newOperation);
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE EXTRA ??? /////////////////////////
+
+// TODO: Operand::location
+
+///////////////////////// VALIDATE OPERATION OPERAND TYPE /////////////////////////
+
+static void mutateOperand(Operand* operand, OperandType type) {
+    Operand newOperand = *operand;
+    newOperand.type = type;
+    switch (type) {
+        case OperandType::FLOAT16:
+        case OperandType::FLOAT32:
+        case OperandType::INT32:
+        case OperandType::UINT32:
+        case OperandType::BOOL:
+            newOperand.dimensions = {};
+            newOperand.scale = 0.0f;
+            newOperand.zeroPoint = 0;
+            break;
+        case OperandType::TENSOR_BOOL8:
+        case OperandType::TENSOR_FLOAT16:
+        case OperandType::TENSOR_FLOAT32:
+            newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions
+                                                                   : std::vector<int32_t>({1});
+            newOperand.scale = 0.0f;
+            newOperand.zeroPoint = 0;
+            break;
+        case OperandType::TENSOR_INT32:
+            newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions
+                                                                   : std::vector<int32_t>({1});
+            newOperand.zeroPoint = 0;
+            break;
+        case OperandType::TENSOR_QUANT8_ASYMM:
+        case OperandType::TENSOR_QUANT8_SYMM:
+        case OperandType::TENSOR_QUANT16_ASYMM:
+        case OperandType::TENSOR_QUANT16_SYMM:
+            newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions
+                                                                   : std::vector<int32_t>({1});
+            newOperand.scale = operand->scale != 0.0f ? operand->scale : 1.0f;
+            break;
+        case OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL: {
+            newOperand.dimensions = operand->dimensions.size() > 0 ? operand->dimensions
+                                                                   : std::vector<int32_t>({1});
+            newOperand.scale = 0.0f;
+            newOperand.zeroPoint = 0;
+
+            SymmPerChannelQuantParams channelQuant;
+            channelQuant.channelDim = 0;
+            channelQuant.scales = std::vector<float>(
+                    operand->dimensions.size() > 0 ? static_cast<size_t>(operand->dimensions[0])
+                                                   : 0);
+            for (size_t i = 0; i < channelQuant.scales.size(); ++i) {
+                channelQuant.scales[i] = 1.0f;
+            }
+            newOperand.extraParams->set<OperandExtraParams::Tag::channelQuant>(
+                    std::move(channelQuant));
+        } break;
+        default:
+            break;
+    }
+    *operand = newOperand;
+}
+
+static bool mutateOperationOperandTypeSkip(size_t operand, OperandType type, const Model& model) {
+    if (type == model.main.operands[operand].type) {
+        return true;
+    }
+    for (const Operation& operation : model.main.operations) {
+        // Skip mutateOperationOperandTypeTest for the following operations.
+        // - LSH_PROJECTION's second argument is allowed to have any type.
+        // - ARGMIN and ARGMAX's first argument can be any of
+        // TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
+        // - CAST's argument can be any of TENSOR_(FLOAT16|FLOAT32|INT32|QUANT8_ASYMM).
+        // - RANDOM_MULTINOMIAL's argument can be either TENSOR_FLOAT16 or TENSOR_FLOAT32.
+        // - DEQUANTIZE input can be any of
+        // TENSOR_(QUANT8_ASYMM|QUANT8_ASYMM_SIGNED|QUANT8_SYMM|QUANT8_SYMM_PER_CHANNEL),
+        // output can be of either TENSOR_FLOAT16 or TENSOR_FLOAT32.
+        // - QUANTIZE input can be either TENSOR_FLOAT16 or TENSOR_FLOAT32
+        // - CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
+        // - DEPTHWISE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
+        // - GROUPED_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
+        // - TRANSPOSE_CONV_2D filter type (arg 1) can be QUANT8_ASYMM or QUANT8_SYMM_PER_CHANNEL
+        // - AXIS_ALIGNED_BBOX_TRANSFORM bounding boxes (arg 1) can be of
+        //     TENSOR_QUANT8_ASYMM or TENSOR_QUANT8_ASYMM_SIGNED.
+        // - RANK's input can have any TENSOR_* type.
+        switch (operation.type) {
+            case OperationType::LSH_PROJECTION: {
+                if (operand == operation.inputs[1]) {
+                    return true;
+                }
+            } break;
+            case OperationType::CAST:
+            case OperationType::ARGMAX:
+            case OperationType::ARGMIN: {
+                if (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
+                    type == OperandType::TENSOR_INT32 || type == OperandType::TENSOR_QUANT8_ASYMM ||
+                    type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED) {
+                    return true;
+                }
+            } break;
+            case OperationType::QUANTIZE: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
+                    return true;
+                }
+                if (operand == operation.outputs[0] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
+                    return true;
+                }
+            } break;
+            case OperationType::RANDOM_MULTINOMIAL: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
+                    return true;
+                }
+            } break;
+            case OperationType::DEQUANTIZE: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED ||
+                     type == OperandType::TENSOR_QUANT8_SYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
+                    return true;
+                }
+                if (operand == operation.outputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32)) {
+                    return true;
+                }
+            } break;
+            case OperationType::TRANSPOSE_CONV_2D:
+            case OperationType::GROUPED_CONV_2D:
+            case OperationType::DEPTHWISE_CONV_2D:
+            case OperationType::CONV_2D: {
+                if (operand == operation.inputs[1] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL)) {
+                    return true;
+                }
+            } break;
+            case OperationType::AXIS_ALIGNED_BBOX_TRANSFORM: {
+                if (operand == operation.inputs[1] &&
+                    (type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
+                    return true;
+                }
+            } break;
+            case OperationType::RANK: {
+                if (operand == operation.inputs[0] &&
+                    (type == OperandType::TENSOR_FLOAT16 || type == OperandType::TENSOR_FLOAT32 ||
+                     type == OperandType::TENSOR_INT32 ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM ||
+                     type == OperandType::TENSOR_QUANT16_SYMM ||
+                     type == OperandType::TENSOR_BOOL8 ||
+                     type == OperandType::TENSOR_QUANT8_SYMM_PER_CHANNEL ||
+                     type == OperandType::TENSOR_QUANT16_ASYMM ||
+                     type == OperandType::TENSOR_QUANT8_SYMM ||
+                     type == OperandType::TENSOR_QUANT8_ASYMM_SIGNED)) {
+                    return true;
+                }
+            } break;
+            default:
+                break;
+        }
+    }
+    return false;
+}
+
+static void mutateOperationOperandTypeTest(const std::shared_ptr<IDevice>& device,
+                                           const Model& model) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        for (OperandType invalidOperandType : ndk::enum_range<OperandType>()) {
+            if (mutateOperationOperandTypeSkip(operand, invalidOperandType, model)) {
+                continue;
+            }
+            const std::string message = "mutateOperationOperandTypeTest: operand " +
+                                        std::to_string(operand) + " set to type " +
+                                        toString(invalidOperandType);
+            validate(device, message, model,
+                     [operand, invalidOperandType](Model* model, ExecutionPreference*, Priority*) {
+                         mutateOperand(&model->main.operands[operand], invalidOperandType);
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE MODEL OPERATION TYPE /////////////////////////
+
+static const int32_t invalidOperationTypes[] = {
+        -1,
+        static_cast<int32_t>(*(ndk::enum_range<OperationType>().end() - 1)) + 1,
+};
+
+static void mutateOperationTypeTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        for (int32_t invalidOperationType : invalidOperationTypes) {
+            const std::string message = "mutateOperationTypeTest: operation " +
+                                        std::to_string(operation) + " set to value " +
+                                        std::to_string(invalidOperationType);
+            validate(device, message, model,
+                     [operation, invalidOperationType](Model* model, ExecutionPreference*,
+                                                       Priority*) {
+                         model->main.operations[operation].type =
+                                 static_cast<OperationType>(invalidOperationType);
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE MODEL OPERATION INPUT OPERAND INDEX /////////////////////////
+
+static void mutateOperationInputOperandIndexTest(const std::shared_ptr<IDevice>& device,
+                                                 const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        const uint32_t invalidOperand = model.main.operands.size();
+        for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
+            const std::string message = "mutateOperationInputOperandIndexTest: operation " +
+                                        std::to_string(operation) + " input " +
+                                        std::to_string(input);
+            validate(device, message, model,
+                     [operation, input, invalidOperand](Model* model, ExecutionPreference*,
+                                                        Priority*) {
+                         model->main.operations[operation].inputs[input] = invalidOperand;
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE MODEL OPERATION OUTPUT OPERAND INDEX /////////////////////////
+
+static void mutateOperationOutputOperandIndexTest(const std::shared_ptr<IDevice>& device,
+                                                  const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        const uint32_t invalidOperand = model.main.operands.size();
+        for (size_t output = 0; output < model.main.operations[operation].outputs.size();
+             ++output) {
+            const std::string message = "mutateOperationOutputOperandIndexTest: operation " +
+                                        std::to_string(operation) + " output " +
+                                        std::to_string(output);
+            validate(device, message, model,
+                     [operation, output, invalidOperand](Model* model, ExecutionPreference*,
+                                                         Priority*) {
+                         model->main.operations[operation].outputs[output] = invalidOperand;
+                     });
+        }
+    }
+}
+
+///////////////////////// VALIDATE MODEL OPERANDS WRITTEN ///////////////////////////////////////
+
+static void mutateOperationRemoveWriteTest(const std::shared_ptr<IDevice>& device,
+                                           const Model& model,
+                                           const std::vector<uint32_t>& numberOfConsumers) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        for (size_t outputNum = 0; outputNum < model.main.operations[operation].outputs.size();
+             ++outputNum) {
+            const uint32_t outputOperandIndex = model.main.operations[operation].outputs[outputNum];
+            if (numberOfConsumers[outputOperandIndex] > 0) {
+                const std::string message = "mutateOperationRemoveWriteTest: operation " +
+                                            std::to_string(operation) + " writes to " +
+                                            std::to_string(outputOperandIndex);
+                validate(device, message, model,
+                         [operation, outputNum](Model* model, ExecutionPreference*, Priority*) {
+                             int32_t& outputOperandIndex =
+                                     model->main.operations[operation].outputs[outputNum];
+                             Operand operandValue = model->main.operands[outputOperandIndex];
+                             if (operandValue.lifetime == OperandLifeTime::SUBGRAPH_OUTPUT) {
+                                 operandValue.lifetime = OperandLifeTime::TEMPORARY_VARIABLE;
+                             } else {
+                                 ASSERT_EQ(operandValue.lifetime,
+                                           OperandLifeTime::TEMPORARY_VARIABLE);
+                             }
+                             outputOperandIndex = model->main.operands.size();
+                             model->main.operands.push_back(operandValue);
+                         });
+            }
+        }
+    }
+}
+
+///////////////////////// REMOVE OPERAND FROM EVERYTHING /////////////////////////
+
+static void removeValueAndDecrementGreaterValues(std::vector<int32_t>* vec, uint32_t value) {
+    if (vec) {
+        // remove elements matching "value"
+        vec->erase(std::remove(vec->begin(), vec->end(), value), vec->end());
+
+        // decrement elements exceeding "value"
+        std::transform(vec->begin(), vec->end(), vec->begin(),
+                       [value](uint32_t v) { return v > value ? v-- : v; });
+    }
+}
+
+static void removeOperand(Model* model, uint32_t index) {
+    model->main.operands.erase(model->main.operands.begin() + index);
+    for (Operation& operation : model->main.operations) {
+        removeValueAndDecrementGreaterValues(&operation.inputs, index);
+        removeValueAndDecrementGreaterValues(&operation.outputs, index);
+    }
+    removeValueAndDecrementGreaterValues(&model->main.inputIndexes, index);
+    removeValueAndDecrementGreaterValues(&model->main.outputIndexes, index);
+}
+
+static bool removeOperandSkip(size_t operandIndex, const Model& model,
+                              const std::vector<uint32_t>& numberOfConsumers) {
+    if (numberOfConsumers[operandIndex] == 0) {
+        // Removing an unused operand has no effect.
+        return true;
+    }
+    for (const Operation& operation : model.main.operations) {
+        // Skip removeOperandTest for the following operations.
+        // - SPLIT's outputs are not checked during prepareModel.
+        if (operation.type == OperationType::SPLIT) {
+            for (const size_t index : operation.outputs) {
+                if (index == operandIndex) {
+                    return true;
+                }
+            }
+        }
+        // BIDIRECTIONAL_SEQUENCE_LSTM and BIDIRECTIONAL_SEQUENCE_RNN can have
+        // either one, two, three or four outputs depending on their
+        // mergeOutputs parameter and if state outputs are provided.
+        // UNIDIRECTIONAL_SEQUENCE_LSTM and UNIDIRECTIONAL_SEQUENCE_RNN can have
+        // either one or three outputs depending on whether state outputs are
+        // provided.
+        if (operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_LSTM ||
+            operation.type == OperationType::UNIDIRECTIONAL_SEQUENCE_RNN ||
+            operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_LSTM ||
+            operation.type == OperationType::BIDIRECTIONAL_SEQUENCE_RNN) {
+            for (const size_t index : operation.outputs) {
+                if (index == operandIndex) {
+                    return true;
+                }
+            }
+        }
+    }
+    return false;
+}
+
+static void removeOperandTest(const std::shared_ptr<IDevice>& device, const Model& model,
+                              const std::vector<uint32_t>& numberOfConsumers) {
+    for (size_t operand = 0; operand < model.main.operands.size(); ++operand) {
+        if (removeOperandSkip(operand, model, numberOfConsumers)) {
+            continue;
+        }
+        const std::string message = "removeOperandTest: operand " + std::to_string(operand);
+        validate(device, message, model, [operand](Model* model, ExecutionPreference*, Priority*) {
+            removeOperand(model, operand);
+        });
+    }
+}
+
+///////////////////////// REMOVE OPERATION /////////////////////////
+
+static void removeOperation(Model* model, uint32_t index) {
+    auto& operations = model->main.operations;
+    operations.erase(operations.begin() + index);
+}
+
+static void removeOperationTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        const std::string message = "removeOperationTest: operation " + std::to_string(operation);
+        validate(device, message, model,
+                 [operation](Model* model, ExecutionPreference*, Priority*) {
+                     removeOperation(model, operation);
+                 });
+    }
+}
+
+///////////////////////// REMOVE OPERATION INPUT /////////////////////////
+
+static bool removeOperationInputSkip(const Operation& op, size_t input) {
+    // Skip removeOperationInputTest for the following operations.
+    // - CONCATENATION has at least 2 inputs, with the last element being INT32.
+    // - CONV_2D, DEPTHWISE_CONV_2D, MAX_POOL_2D, AVERAGE_POOL_2D, L2_POOL_2D, RESIZE_BILINEAR,
+    //   SPACE_TO_DEPTH, SPACE_TO_DEPTH, SPACE_TO_BATCH_ND, BATCH_TO_SPACE_ND can have an optional
+    //   layout parameter.
+    //   RESIZE_BILINEAR and RESIZE_NEAREST_NEIGHBOR can have optional
+    //   align_corners and half_pixel_centers parameters.
+    // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional axis
+    //   parameter.
+    switch (op.type) {
+        case OperationType::CONCATENATION: {
+            if (op.inputs.size() > 2 && input != op.inputs.size() - 1) {
+                return true;
+            }
+        } break;
+        case OperationType::DEPTHWISE_CONV_2D: {
+            if ((op.inputs.size() == 12 && input == 11) || (op.inputs.size() == 9 && input == 8)) {
+                return true;
+            }
+        } break;
+        case OperationType::CONV_2D:
+        case OperationType::AVERAGE_POOL_2D:
+        case OperationType::MAX_POOL_2D:
+        case OperationType::L2_POOL_2D: {
+            if ((op.inputs.size() == 11 && input == 10) || (op.inputs.size() == 8 && input == 7)) {
+                return true;
+            }
+        } break;
+        case OperationType::RESIZE_BILINEAR: {
+            if (op.inputs.size() >= 4 && input >= 3) {
+                return true;
+            }
+        } break;
+        case OperationType::RESIZE_NEAREST_NEIGHBOR: {
+            if (op.inputs.size() >= 5 && input >= 3) {
+                return true;
+            }
+        } break;
+        case OperationType::SPACE_TO_DEPTH:
+        case OperationType::DEPTH_TO_SPACE:
+        case OperationType::BATCH_TO_SPACE_ND: {
+            if (op.inputs.size() == 3 && input == 2) {
+                return true;
+            }
+        } break;
+        case OperationType::SPACE_TO_BATCH_ND: {
+            if (op.inputs.size() == 4 && input == 3) {
+                return true;
+            }
+        } break;
+        case OperationType::L2_NORMALIZATION: {
+            if (op.inputs.size() == 2 && input == 1) {
+                return true;
+            }
+        } break;
+        case OperationType::LOCAL_RESPONSE_NORMALIZATION: {
+            if (op.inputs.size() == 6 && input == 5) {
+                return true;
+            }
+        } break;
+        case OperationType::SOFTMAX: {
+            if (op.inputs.size() == 3 && input == 2) {
+                return true;
+            }
+        } break;
+        default:
+            break;
+    }
+    return false;
+}
+
+static void removeOperationInputTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        for (size_t input = 0; input < model.main.operations[operation].inputs.size(); ++input) {
+            const Operation& op = model.main.operations[operation];
+            if (removeOperationInputSkip(op, input)) {
+                continue;
+            }
+            const std::string message = "removeOperationInputTest: operation " +
+                                        std::to_string(operation) + ", input " +
+                                        std::to_string(input);
+            validate(device, message, model,
+                     [operation, input](Model* model, ExecutionPreference*, Priority*) {
+                         auto& inputs = model->main.operations[operation].inputs;
+                         inputs.erase(inputs.begin() + input);
+                     });
+        }
+    }
+}
+
+///////////////////////// REMOVE OPERATION OUTPUT /////////////////////////
+
+static void removeOperationOutputTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        for (size_t output = 0; output < model.main.operations[operation].outputs.size();
+             ++output) {
+            const std::string message = "removeOperationOutputTest: operation " +
+                                        std::to_string(operation) + ", output " +
+                                        std::to_string(output);
+            validate(device, message, model,
+                     [operation, output](Model* model, ExecutionPreference*, Priority*) {
+                         auto& outputs = model->main.operations[operation].outputs;
+                         outputs.erase(outputs.begin() + output);
+                     });
+        }
+    }
+}
+
+///////////////////////// MODEL VALIDATION /////////////////////////
+
+// TODO: remove model input
+// TODO: remove model output
+// TODO: add unused operation
+
+///////////////////////// ADD OPERATION INPUT /////////////////////////
+
+static bool addOperationInputSkip(const Operation& op) {
+    // Skip addOperationInputTest for the following operations.
+    // - L2_NORMALIZATION, LOCAL_RESPONSE_NORMALIZATION, SOFTMAX can have an optional INT32 axis
+    //   parameter.
+    if ((op.type == OperationType::L2_NORMALIZATION && op.inputs.size() == 1) ||
+        (op.type == OperationType::LOCAL_RESPONSE_NORMALIZATION && op.inputs.size() == 5) ||
+        (op.type == OperationType::SOFTMAX && op.inputs.size() == 2) ||
+        (op.type == OperationType::RESIZE_BILINEAR && op.inputs.size() < 6) ||
+        (op.type == OperationType::RESIZE_NEAREST_NEIGHBOR && op.inputs.size() < 6)) {
+        return true;
+    }
+    return false;
+}
+
+static void addOperationInputTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        if (addOperationInputSkip(model.main.operations[operation])) {
+            continue;
+        }
+        const std::string message = "addOperationInputTest: operation " + std::to_string(operation);
+        validate(device, message, model,
+                 [operation](Model* model, ExecutionPreference*, Priority*) {
+                     uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_INPUT);
+                     model->main.operations[operation].inputs.push_back(index);
+                     model->main.inputIndexes.push_back(index);
+                 });
+    }
+}
+
+///////////////////////// ADD OPERATION OUTPUT /////////////////////////
+
+static void addOperationOutputTest(const std::shared_ptr<IDevice>& device, const Model& model) {
+    for (size_t operation = 0; operation < model.main.operations.size(); ++operation) {
+        const std::string message =
+                "addOperationOutputTest: operation " + std::to_string(operation);
+        validate(device, message, model,
+                 [operation](Model* model, ExecutionPreference*, Priority*) {
+                     uint32_t index = addOperand(model, OperandLifeTime::SUBGRAPH_OUTPUT);
+                     model->main.operations[operation].outputs.push_back(index);
+                     model->main.outputIndexes.push_back(index);
+                 });
+    }
+}
+
+///////////////////////// VALIDATE EXECUTION PREFERENCE /////////////////////////
+
+static const int32_t invalidExecutionPreferences[] = {
+        static_cast<int32_t>(ExecutionPreference::LOW_POWER) - 1,        // lower bound
+        static_cast<int32_t>(ExecutionPreference::SUSTAINED_SPEED) + 1,  // upper bound
+};
+
+static void mutateExecutionPreferenceTest(const std::shared_ptr<IDevice>& device,
+                                          const Model& model) {
+    for (int32_t invalidPreference : invalidExecutionPreferences) {
+        const std::string message =
+                "mutateExecutionPreferenceTest: preference " + std::to_string(invalidPreference);
+        validate(device, message, model,
+                 [invalidPreference](Model*, ExecutionPreference* preference, Priority*) {
+                     *preference = static_cast<ExecutionPreference>(invalidPreference);
+                 });
+    }
+}
+
+///////////////////////// VALIDATE PRIORITY /////////////////////////
+
+static const int32_t invalidPriorities[] = {
+        static_cast<int32_t>(Priority::LOW) - 1,   // lower bound
+        static_cast<int32_t>(Priority::HIGH) + 1,  // upper bound
+};
+
+static void mutateExecutionPriorityTest(const std::shared_ptr<IDevice>& device,
+                                        const Model& model) {
+    for (int32_t invalidPriority : invalidPriorities) {
+        const std::string message =
+                "mutatePriorityTest: priority " + std::to_string(invalidPriority);
+        validate(device, message, model,
+                 [invalidPriority](Model*, ExecutionPreference*, Priority* priority) {
+                     *priority = static_cast<Priority>(invalidPriority);
+                 });
+    }
+}
+
+////////////////////////// ENTRY POINT //////////////////////////////
+
+void validateModel(const std::shared_ptr<IDevice>& device, const Model& model) {
+    const auto numberOfConsumers =
+            nn::countNumberOfConsumers(model.main.operands.size(),
+                                       nn::unvalidatedConvert(model.main.operations).value())
+                    .value();
+    mutateExecutionOrderTest(device, model, numberOfConsumers);
+    mutateOperandTypeTest(device, model);
+    mutateOperandRankTest(device, model);
+    mutateOperandScaleTest(device, model);
+    mutateOperandZeroPointTest(device, model);
+    mutateOperandLifeTimeTest(device, model);
+    mutateOperandInputOutputTest(device, model);
+    mutateOperandAddWriterTest(device, model);
+    mutateOperationOperandTypeTest(device, model);
+    mutateOperationTypeTest(device, model);
+    mutateOperationInputOperandIndexTest(device, model);
+    mutateOperationOutputOperandIndexTest(device, model);
+    mutateOperationRemoveWriteTest(device, model, numberOfConsumers);
+    removeOperandTest(device, model, numberOfConsumers);
+    removeOperationTest(device, model);
+    removeOperationInputTest(device, model);
+    removeOperationOutputTest(device, model);
+    addOperationInputTest(device, model);
+    addOperationOutputTest(device, model);
+    mutateExecutionPreferenceTest(device, model);
+    mutateExecutionPriorityTest(device, model);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp
new file mode 100644
index 0000000..29e2471
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/ValidateRequest.cpp
@@ -0,0 +1,215 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+
+#include <aidl/android/hardware/neuralnetworks/RequestMemoryPool.h>
+#include <android/binder_auto_utils.h>
+#include <variant>
+
+#include <chrono>
+
+#include <TestHarness.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+#include "VtsHalNeuralnetworks.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using ExecutionMutation = std::function<void(Request*)>;
+
+///////////////////////// UTILITY FUNCTIONS /////////////////////////
+
+// Primary validation function. This function will take a valid request, apply a
+// mutation to it to invalidate the request, then pass it to interface calls
+// that use the request.
+static void validate(const std::shared_ptr<IPreparedModel>& preparedModel,
+                     const std::string& message, const Request& originalRequest,
+                     const ExecutionMutation& mutate) {
+    Request request = utils::clone(originalRequest).value();
+    mutate(&request);
+
+    // We'd like to test both with timing requested and without timing
+    // requested. Rather than running each test both ways, we'll decide whether
+    // to request timing by hashing the message. We do not use std::hash because
+    // it is not guaranteed stable across executions.
+    char hash = 0;
+    for (auto c : message) {
+        hash ^= c;
+    };
+    bool measure = (hash & 1);
+
+    // synchronous
+    {
+        SCOPED_TRACE(message + " [executeSynchronously]");
+        ExecutionResult executionResult;
+        const auto executeStatus = preparedModel->executeSynchronously(
+                request, measure, kNoDeadline, kOmittedTimeoutDuration, &executionResult);
+        ASSERT_FALSE(executeStatus.isOk());
+        ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+        ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+                  ErrorStatus::INVALID_ARGUMENT);
+    }
+
+    // fenced
+    {
+        SCOPED_TRACE(message + " [executeFenced]");
+        FencedExecutionResult executionResult;
+        const auto executeStatus = preparedModel->executeFenced(request, {}, false, kNoDeadline,
+                                                                kOmittedTimeoutDuration,
+                                                                kNoDuration, &executionResult);
+        ASSERT_FALSE(executeStatus.isOk());
+        ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+        ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+                  ErrorStatus::INVALID_ARGUMENT);
+    }
+
+    // burst
+    {
+        SCOPED_TRACE(message + " [burst]");
+
+        // create burst
+        std::shared_ptr<IBurst> burst;
+        auto ret = preparedModel->configureExecutionBurst(&burst);
+        ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+        ASSERT_NE(nullptr, burst.get());
+
+        // use -1 for all memory identifier tokens
+        const std::vector<int64_t> slots(request.pools.size(), -1);
+
+        ExecutionResult executionResult;
+        const auto executeStatus = burst->executeSynchronously(
+                request, slots, measure, kNoDeadline, kOmittedTimeoutDuration, &executionResult);
+        ASSERT_FALSE(executeStatus.isOk());
+        ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+        ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+                  ErrorStatus::INVALID_ARGUMENT);
+    }
+}
+
+std::shared_ptr<IBurst> createBurst(const std::shared_ptr<IPreparedModel>& preparedModel) {
+    std::shared_ptr<IBurst> burst;
+    const auto ret = preparedModel->configureExecutionBurst(&burst);
+    if (!ret.isOk()) return nullptr;
+    return burst;
+}
+
+///////////////////////// REMOVE INPUT ////////////////////////////////////
+
+static void removeInputTest(const std::shared_ptr<IPreparedModel>& preparedModel,
+                            const Request& request) {
+    for (size_t input = 0; input < request.inputs.size(); ++input) {
+        const std::string message = "removeInput: removed input " + std::to_string(input);
+        validate(preparedModel, message, request, [input](Request* request) {
+            request->inputs.erase(request->inputs.begin() + input);
+        });
+    }
+}
+
+///////////////////////// REMOVE OUTPUT ////////////////////////////////////
+
+static void removeOutputTest(const std::shared_ptr<IPreparedModel>& preparedModel,
+                             const Request& request) {
+    for (size_t output = 0; output < request.outputs.size(); ++output) {
+        const std::string message = "removeOutput: removed Output " + std::to_string(output);
+        validate(preparedModel, message, request, [output](Request* request) {
+            request->outputs.erase(request->outputs.begin() + output);
+        });
+    }
+}
+
+///////////////////////////// ENTRY POINT //////////////////////////////////
+
+void validateRequest(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request) {
+    removeInputTest(preparedModel, request);
+    removeOutputTest(preparedModel, request);
+}
+
+void validateBurst(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request) {
+    // create burst
+    std::shared_ptr<IBurst> burst;
+    auto ret = preparedModel->configureExecutionBurst(&burst);
+    ASSERT_TRUE(ret.isOk()) << ret.getDescription();
+    ASSERT_NE(nullptr, burst.get());
+
+    const auto test = [&burst, &request](const std::vector<int64_t>& slots) {
+        ExecutionResult executionResult;
+        const auto executeStatus =
+                burst->executeSynchronously(request, slots, /*measure=*/false, kNoDeadline,
+                                            kOmittedTimeoutDuration, &executionResult);
+        ASSERT_FALSE(executeStatus.isOk());
+        ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+        ASSERT_EQ(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()),
+                  ErrorStatus::INVALID_ARGUMENT);
+    };
+
+    int64_t currentSlot = 0;
+    std::vector<int64_t> slots;
+    slots.reserve(request.pools.size());
+    for (const auto& pool : request.pools) {
+        if (pool.getTag() == RequestMemoryPool::Tag::pool) {
+            slots.push_back(currentSlot++);
+        } else {
+            slots.push_back(-1);
+        }
+    }
+
+    constexpr int64_t invalidSlot = -2;
+
+    // validate failure when invalid memory identifier token value
+    for (size_t i = 0; i < request.pools.size(); ++i) {
+        const int64_t oldSlotValue = slots[i];
+
+        slots[i] = invalidSlot;
+        test(slots);
+
+        slots[i] = oldSlotValue;
+    }
+
+    // validate failure when request.pools.size() != memoryIdentifierTokens.size()
+    if (request.pools.size() > 0) {
+        slots = std::vector<int64_t>(request.pools.size() - 1, -1);
+        test(slots);
+    }
+
+    // validate failure when request.pools.size() != memoryIdentifierTokens.size()
+    slots = std::vector<int64_t>(request.pools.size() + 1, -1);
+    test(slots);
+
+    // validate failure when invalid memory identifier token value
+    const auto freeStatus = burst->releaseMemoryResource(invalidSlot);
+    ASSERT_FALSE(freeStatus.isOk());
+    ASSERT_EQ(freeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    ASSERT_EQ(static_cast<ErrorStatus>(freeStatus.getServiceSpecificError()),
+              ErrorStatus::INVALID_ARGUMENT);
+}
+
+void validateRequestFailure(const std::shared_ptr<IPreparedModel>& preparedModel,
+                            const Request& request) {
+    SCOPED_TRACE("Expecting request to fail [executeSynchronously]");
+    ExecutionResult executionResult;
+    const auto executeStatus = preparedModel->executeSynchronously(
+            request, false, kNoDeadline, kOmittedTimeoutDuration, &executionResult);
+
+    ASSERT_FALSE(executeStatus.isOk());
+    ASSERT_EQ(executeStatus.getExceptionCode(), EX_SERVICE_SPECIFIC);
+    ASSERT_NE(static_cast<ErrorStatus>(executeStatus.getServiceSpecificError()), ErrorStatus::NONE);
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
new file mode 100644
index 0000000..ee7cf89
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.cpp
@@ -0,0 +1,195 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "neuralnetworks_aidl_hal_test"
+#include "VtsHalNeuralnetworks.h"
+
+#include <android-base/logging.h>
+#include <android/binder_auto_utils.h>
+#include <android/binder_interface_utils.h>
+#include <android/binder_manager.h>
+#include <android/binder_status.h>
+#include <gtest/gtest.h>
+#include <memory>
+#include <string>
+#include <utility>
+
+#include <TestHarness.h>
+#include <aidl/Vintf.h>
+#include <nnapi/hal/aidl/Conversions.h>
+
+#include "Callbacks.h"
+#include "GeneratedTestHarness.h"
+#include "Utils.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using implementation::PreparedModelCallback;
+
+// internal helper function
+void createPreparedModel(const std::shared_ptr<IDevice>& device, const Model& model,
+                         std::shared_ptr<IPreparedModel>* preparedModel, bool reportSkipping) {
+    ASSERT_NE(nullptr, preparedModel);
+    *preparedModel = nullptr;
+
+    // see if service can handle model
+    std::vector<bool> supportedOperations;
+    const auto supportedCallStatus = device->getSupportedOperations(model, &supportedOperations);
+    ASSERT_TRUE(supportedCallStatus.isOk());
+    ASSERT_NE(0ul, supportedOperations.size());
+    const bool fullySupportsModel = std::all_of(
+            supportedOperations.begin(), supportedOperations.end(), [](bool v) { return v; });
+
+    // launch prepare model
+    const std::shared_ptr<PreparedModelCallback> preparedModelCallback =
+            ndk::SharedRefBase::make<PreparedModelCallback>();
+    const auto prepareLaunchStatus =
+            device->prepareModel(model, ExecutionPreference::FAST_SINGLE_ANSWER, kDefaultPriority,
+                                 kNoDeadline, {}, {}, kEmptyCacheToken, preparedModelCallback);
+    ASSERT_TRUE(prepareLaunchStatus.isOk()) << prepareLaunchStatus.getDescription();
+
+    // retrieve prepared model
+    preparedModelCallback->wait();
+    const ErrorStatus prepareReturnStatus = preparedModelCallback->getStatus();
+    *preparedModel = preparedModelCallback->getPreparedModel();
+
+    // The getSupportedOperations call returns a list of operations that are guaranteed not to fail
+    // if prepareModel is called, and 'fullySupportsModel' is true i.f.f. the entire model is
+    // guaranteed. If a driver has any doubt that it can prepare an operation, it must return false.
+    // So here, if a driver isn't sure if it can support an operation, but reports that it
+    // successfully prepared the model, the test can continue.
+    if (!fullySupportsModel && prepareReturnStatus != ErrorStatus::NONE) {
+        ASSERT_EQ(nullptr, preparedModel->get());
+        if (!reportSkipping) {
+            return;
+        }
+        LOG(INFO) << "NN VTS: Early termination of test because vendor service cannot prepare "
+                     "model that it does not support.";
+        std::cout << "[          ]   Early termination of test because vendor service cannot "
+                     "prepare model that it does not support."
+                  << std::endl;
+        GTEST_SKIP();
+    }
+
+    ASSERT_EQ(ErrorStatus::NONE, prepareReturnStatus);
+    ASSERT_NE(nullptr, preparedModel->get());
+}
+
+void NeuralNetworksAidlTest::SetUp() {
+    testing::TestWithParam<NeuralNetworksAidlTestParam>::SetUp();
+    ASSERT_NE(kDevice, nullptr);
+}
+
+static NamedDevice makeNamedDevice(const std::string& name) {
+    ndk::SpAIBinder binder(AServiceManager_waitForService(name.c_str()));
+    return {name, IDevice::fromBinder(binder)};
+}
+
+static std::vector<NamedDevice> getNamedDevicesImpl() {
+    // Retrieves the name of all service instances that implement IDevice,
+    // including any Lazy HAL instances.
+    const std::vector<std::string> names = ::android::getAidlHalInstanceNames(IDevice::descriptor);
+
+    // Get a handle to each device and pair it with its name.
+    std::vector<NamedDevice> namedDevices;
+    namedDevices.reserve(names.size());
+    std::transform(names.begin(), names.end(), std::back_inserter(namedDevices), makeNamedDevice);
+    return namedDevices;
+}
+
+const std::vector<NamedDevice>& getNamedDevices() {
+    const static std::vector<NamedDevice> devices = getNamedDevicesImpl();
+    return devices;
+}
+
+std::string printNeuralNetworksAidlTest(
+        const testing::TestParamInfo<NeuralNetworksAidlTestParam>& info) {
+    return gtestCompliantName(getName(info.param));
+}
+
+INSTANTIATE_DEVICE_TEST(NeuralNetworksAidlTest);
+
+// Forward declaration from ValidateModel.cpp
+void validateModel(const std::shared_ptr<IDevice>& device, const Model& model);
+// Forward declaration from ValidateRequest.cpp
+void validateRequest(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request);
+// Forward declaration from ValidateRequest.cpp
+void validateBurst(const std::shared_ptr<IPreparedModel>& preparedModel, const Request& request);
+// Forward declaration from ValidateRequest.cpp
+void validateRequestFailure(const std::shared_ptr<IPreparedModel>& preparedModel,
+                            const Request& request);
+
+void validateEverything(const std::shared_ptr<IDevice>& device, const Model& model,
+                        const Request& request) {
+    validateModel(device, model);
+
+    // Create IPreparedModel.
+    std::shared_ptr<IPreparedModel> preparedModel;
+    createPreparedModel(device, model, &preparedModel);
+    if (preparedModel == nullptr) return;
+
+    validateRequest(preparedModel, request);
+    validateBurst(preparedModel, request);
+    // HIDL also had test that expected executeFenced to fail on received null fd (-1). This is not
+    // allowed in AIDL and will result in EX_TRANSACTION_FAILED.
+}
+
+void validateFailure(const std::shared_ptr<IDevice>& device, const Model& model,
+                     const Request& request) {
+    // TODO: Should this always succeed?
+    //       What if the invalid input is part of the model (i.e., a parameter).
+    validateModel(device, model);
+
+    // Create IPreparedModel.
+    std::shared_ptr<IPreparedModel> preparedModel;
+    createPreparedModel(device, model, &preparedModel);
+    if (preparedModel == nullptr) return;
+
+    validateRequestFailure(preparedModel, request);
+}
+
+TEST_P(ValidationTest, Test) {
+    const Model model = createModel(kTestModel);
+    ExecutionContext context;
+    const Request request = context.createRequest(kTestModel);
+    if (kTestModel.expectFailure) {
+        validateFailure(kDevice, model, request);
+    } else {
+        validateEverything(kDevice, model, request);
+    }
+}
+
+INSTANTIATE_GENERATED_TEST(ValidationTest, [](const std::string& testName) {
+    // Skip validation for the "inputs_as_internal" and "all_tensors_as_inputs"
+    // generated tests.
+    return testName.find("inputs_as_internal") == std::string::npos &&
+           testName.find("all_tensors_as_inputs") == std::string::npos;
+});
+
+std::string toString(Executor executor) {
+    switch (executor) {
+        case Executor::SYNC:
+            return "SYNC";
+        case Executor::BURST:
+            return "BURST";
+        case Executor::FENCED:
+            return "FENCED";
+        default:
+            CHECK(false);
+    }
+}
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
diff --git a/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h
new file mode 100644
index 0000000..4312d3a
--- /dev/null
+++ b/neuralnetworks/aidl/vts/functional/VtsHalNeuralnetworks.h
@@ -0,0 +1,61 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_NEURALNETWORKS_AIDL_VTS_HAL_NEURALNETWORKS_H
+#define ANDROID_HARDWARE_NEURALNETWORKS_AIDL_VTS_HAL_NEURALNETWORKS_H
+
+#include <gtest/gtest.h>
+#include <vector>
+
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+
+#include "Callbacks.h"
+#include "Utils.h"
+
+namespace aidl::android::hardware::neuralnetworks::vts::functional {
+
+using NamedDevice = Named<std::shared_ptr<IDevice>>;
+using NeuralNetworksAidlTestParam = NamedDevice;
+
+class NeuralNetworksAidlTest : public testing::TestWithParam<NeuralNetworksAidlTestParam> {
+  protected:
+    void SetUp() override;
+    const std::shared_ptr<IDevice> kDevice = getData(GetParam());
+};
+
+const std::vector<NamedDevice>& getNamedDevices();
+
+std::string printNeuralNetworksAidlTest(
+        const testing::TestParamInfo<NeuralNetworksAidlTestParam>& info);
+
+#define INSTANTIATE_DEVICE_TEST(TestSuite)                                                 \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TestSuite);                              \
+    INSTANTIATE_TEST_SUITE_P(PerInstance, TestSuite, testing::ValuesIn(getNamedDevices()), \
+                             printNeuralNetworksAidlTest)
+
+// Create an IPreparedModel object. If the model cannot be prepared,
+// "preparedModel" will be nullptr instead.
+void createPreparedModel(const std::shared_ptr<IDevice>& device, const Model& model,
+                         std::shared_ptr<IPreparedModel>* preparedModel,
+                         bool reportSkipping = true);
+
+enum class Executor { SYNC, BURST, FENCED };
+
+std::string toString(Executor executor);
+
+}  // namespace aidl::android::hardware::neuralnetworks::vts::functional
+
+#endif  // ANDROID_HARDWARE_NEURALNETWORKS_AIDL_VTS_HAL_NEURALNETWORKS_H
diff --git a/neuralnetworks/utils/OWNERS b/neuralnetworks/utils/OWNERS
new file mode 100644
index 0000000..e4feee3
--- /dev/null
+++ b/neuralnetworks/utils/OWNERS
@@ -0,0 +1,11 @@
+# Neuralnetworks team
+butlermichael@google.com
+dgross@google.com
+galarragas@google.com
+jeanluc@google.com
+levp@google.com
+miaowang@google.com
+pszczepaniak@google.com
+slavash@google.com
+vddang@google.com
+xusongw@google.com
diff --git a/neuralnetworks/utils/README.md b/neuralnetworks/utils/README.md
new file mode 100644
index 0000000..ffad6ee
--- /dev/null
+++ b/neuralnetworks/utils/README.md
@@ -0,0 +1,126 @@
+# NNAPI Conversions
+
+`convert` fails if either the source type or the destination type is invalid, and it yields a valid
+object if the conversion succeeds. For example, let's say that an enumeration in the current version
+has fewer possible values than the "same" canonical enumeration, such as `OperationType`. The new
+value of `HARD_SWISH` (introduced in Android R / NN HAL 1.3) does not map to any valid existing
+value in `OperationType`, but an older value of `ADD` (introduced in Android OC-MR1 / NN HAL 1.0) is
+valid. This can be seen in the following model conversions:
+
+```cpp
+// Unsuccessful conversion
+const nn::Model canonicalModel = createModelWhichHasV1_3Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+EXPECT_FALSE(maybeVersionedModel.has_value());
+```
+```cpp
+// Successful conversion
+const nn::Model canonicalModel = createModelWhichHasOnlyV1_0Operations();
+const nn::Result<V1_0::Model> maybeVersionedModel = V1_0::utils::convert(canonicalModel);
+ASSERT_TRUE(maybeVersionedModel.has_value());
+const V1_0::Model& versionedModel = maybeVersionedModel.value();
+EXPECT_TRUE(V1_0::utils::valid(versionedModel));
+```
+
+`V1_X::utils::convert` does not guarantee that all information is preserved. For example, In the
+case of `nn::ErrorStatus`, the new value of `MISSED_DEADLINE_TRANSIENT` can be represented by the
+existing value of `V1_0::GENERAL_FAILURE`:
+
+```cpp
+// Lossy Canonical -> HAL -> Canonical conversion
+const nn::ErrorStatus canonicalBefore = nn::ErrorStatus::MISSED_DEADLINE_TRANSIENT;
+const V1_0::ErrorStatus versioned = V1_0::utils::convert(canonicalBefore).value();
+const nn::ErrorStatus canonicalAfter = nn::convert(versioned).value();
+EXPECT_NE(canonicalBefore, canonicalAfter);
+```
+
+However, `nn::convert` is guaranteed to preserve all information:
+
+```cpp
+// Lossless HAL -> Canonical -> HAL conversion
+const V1_0::ErrorStatus versionedBefore = V1_0::ErrorStatus::GENERAL_FAILURE;
+const nn::ErrorStatus canonical = nn::convert(versionedBefore).value();
+const V1_0::ErrorStatus versionedAfter = V1_0::utils::convert(canonical).value();
+EXPECT_EQ(versionedBefore, versionedAfter);
+```
+
+The `convert` functions operate only on types that are used in a HIDL method call directly. The
+`unvalidatedConvert` functions operate on types that are either used in a HIDL method call directly
+(i.e., not as a nested class) or used in a subsequent version of the NN HAL. Prefer using `convert`
+over `unvalidatedConvert`.
+
+# Interface Lifetimes across Processes
+
+## HIDL
+
+Some notes about HIDL interface objects and lifetimes across processes:
+
+All HIDL interface objects inherit from `IBase`, which itself inherits from `::android::RefBase`. As
+such, all HIDL interface objects are reference counted and must be owned through `::android::sp` (or
+referenced through `::android::wp`). Allocating `RefBase` objects on the stack will log errors and
+may result in crashes, and deleting a `RefBase` object through another means (e.g., "delete",
+"free", or RAII-cleanup through `std::unique_ptr` or some equivalent) will result in double-free
+and/or use-after-free undefined behavior.
+
+HIDL/Binder manages the reference count of HIDL interface objects automatically across processes. If
+a process that references (but did not create) the HIDL interface object dies, HIDL/Binder ensures
+any reference count it held is properly released. (Caveat: it might be possible that HIDL/Binder
+behave strangely with `::android::wp` references.)
+
+If the process which created the HIDL interface object dies, any call on this object from another
+process will result in a HIDL transport error with the code `DEAD_OBJECT`.
+
+## AIDL
+
+We use NDK backend for AIDL interfaces. Handling of lifetimes is generally the same with the
+following differences:
+* Interfaces inherit from `ndk::ICInterface`, which inherits from `ndk::SharedRefBase`. The latter
+  is an analog of `::android::RefBase` using `std::shared_ptr` for reference counting.
+* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
+  `binder_exception_t`. In case the call is made on a dead object, the call will return
+  `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
+  `STATUS_DEAD_OBJECT`.
+
+# Protecting Asynchronous Calls
+
+## Across HIDL
+
+Some notes about asynchronous calls across HIDL:
+
+For synchronous calls across HIDL, if an error occurs after the function was called but before it
+returns, HIDL will return a transport error. For example, if the message cannot be delivered to the
+server process or if the server process dies before returning a result, HIDL will return from the
+function with the appropriate transport error in the `Return<>` object, which can be queried with
+`Return<>::isOk()`, `Return<>::isDeadObject()`, `Return<>::description()`, etc.
+
+However, HIDL offers no such error management in the case of asynchronous calls. By default, if the
+client launches an asynchronous task and the server fails to return a result through the callback,
+the client will be left waiting indefinitely for a result it will never receive.
+
+In the NNAPI, `IDevice::prepareModel*` and `IPreparedModel::execute*` (but not
+`IPreparedModel::executeSynchronously*`) are asynchronous calls across HIDL. Specifically, these
+asynchronous functions are called with a HIDL interface callback object (`IPrepareModelCallback` for
+`IDevice::prepareModel*` and `IExecutionCallback` for `IPreparedModel::execute*`) and are expected
+to quickly return, and the results are returned at a later time through these callback objects.
+
+To protect against the case when the server dies after the asynchronous task was called successfully
+but before the results could be returned, HIDL provides an object called a "`hidl_death_recipient`,"
+which can be used to detect when an interface object (and more generally, the server process) has
+died. nnapi/hal/ProtectCallback.h's `DeathHandler` uses `hidl_death_recipient`s to detect when the
+driver process has died, and `DeathHandler` will unblock any thread waiting on the results of an
+`IProtectedCallback` callback object that may otherwise not be signaled. In order for this to work,
+the `IProtectedCallback` object must have been registered via `DeathHandler::protectCallback()`.
+
+## Across AIDL
+
+We use NDK backend for AIDL interfaces. Handling of asynchronous calls is generally the same with
+the following differences:
+* AIDL calls return `ndk::ScopedAStatus` which wraps fields of types `binder_status_t` and
+  `binder_exception_t`. In case the call is made on a dead object, the call will return
+  `ndk::ScopedAStatus` with exception `EX_TRANSACTION_FAILED` and binder status
+  `STATUS_DEAD_OBJECT`.
+* AIDL interface doesn't contain asynchronous `IPreparedModel::execute`.
+* Service death is handled using `AIBinder_DeathRecipient` object which is linked to an interface
+  object using `AIBinder_linkToDeath`. nnapi/hal/aidl/ProtectCallback.h provides `DeathHandler`
+  object that is a direct analog of HIDL `DeathHandler`, only using libbinder_ndk objects for
+  implementation.
diff --git a/neuralnetworks/utils/adapter/Android.bp b/neuralnetworks/utils/adapter/Android.bp
new file mode 100644
index 0000000..d073106
--- /dev/null
+++ b/neuralnetworks/utils/adapter/Android.bp
@@ -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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_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/Android.bp b/neuralnetworks/utils/common/Android.bp
new file mode 100644
index 0000000..2ed1e40
--- /dev/null
+++ b/neuralnetworks/utils/common/Android.bp
@@ -0,0 +1,68 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_common",
+    defaults: ["neuralnetworks_utils_defaults"],
+    srcs: ["src/*"],
+    local_include_dirs: ["include/nnapi/hal"],
+    export_include_dirs: ["include"],
+    cflags: ["-Wthread-safety"],
+    static_libs: [
+        "libarect",
+        "neuralnetworks_types",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks-V1-ndk_platform",
+        "libhidlbase",
+        "libnativewindow",
+        "libbinder_ndk",
+    ],
+}
+
+cc_test {
+    name: "neuralnetworks_utils_hal_common_test",
+    srcs: ["test/*.cpp"],
+    static_libs: [
+        "android.hardware.neuralnetworks@1.0",
+        "libgmock",
+        "libneuralnetworks_common",
+        "neuralnetworks_types",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hidl.allocator@1.0",
+        "android.hidl.memory@1.0",
+        "libbase",
+        "libcutils",
+        "libfmq",
+        "libhidlbase",
+        "libhidlmemory",
+        "liblog",
+        "libnativewindow",
+        "libutils",
+    ],
+    test_suites: ["general-tests"],
+}
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
new file mode 100644
index 0000000..702ee92
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/CommonUtils.h
@@ -0,0 +1,142 @@
+/*
+ * 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_COMMON_UTILS_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
+
+#include <cutils/native_handle.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/Types.h>
+#include <functional>
+#include <vector>
+
+// Shorthands
+namespace android::hardware::neuralnetworks {
+namespace hal = ::android::hardware::neuralnetworks;
+}  // namespace android::hardware::neuralnetworks
+
+// Shorthands
+namespace aidl::android::hardware::neuralnetworks {
+namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
+namespace hal = ::android::hardware::neuralnetworks;
+namespace nn = ::android::nn;
+}  // namespace aidl::android::hardware::neuralnetworks
+
+// Shorthands
+namespace android::nn {
+namespace hal = ::android::hardware::neuralnetworks;
+namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
+}  // namespace android::nn
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
+        const nn::Capabilities::PerformanceInfo& float32Performance,
+        const nn::Capabilities::PerformanceInfo& quantized8Performance);
+
+// Indicates if the object contains no pointer-based data that could be relocated to shared memory.
+bool hasNoPointerData(const nn::Model& model);
+bool hasNoPointerData(const nn::Request& request);
+
+// Relocate pointer-based data to shared memory. If `model` has no Operand::LifeTime::POINTER data,
+// the function returns with a reference to `model`. If `model` has Operand::LifeTime::POINTER data,
+// the model is copied to `maybeModelInSharedOut` with the POINTER data relocated to a memory pool,
+// and the function returns with a reference to `*maybeModelInSharedOut`.
+nn::GeneralResult<std::reference_wrapper<const nn::Model>> flushDataFromPointerToShared(
+        const nn::Model* model, std::optional<nn::Model>* maybeModelInSharedOut);
+
+// Record a relocation mapping between pointer-based data and shared memory.
+// Only two specializations of this template may exist:
+// - RelocationInfo<const void*> for request inputs
+// - RelocationInfo<void*> for request outputs
+template <typename PointerType>
+struct RelocationInfo {
+    PointerType data;
+    size_t length;
+    size_t offset;
+};
+using InputRelocationInfo = RelocationInfo<const void*>;
+using OutputRelocationInfo = RelocationInfo<void*>;
+
+// Keep track of the relocation mapping between pointer-based data and shared memory pool,
+// and provide method to copy the data between pointers and the shared memory pool.
+// Only two specializations of this template may exist:
+// - RelocationTracker<InputRelocationInfo> for request inputs
+// - RelocationTracker<OutputRelocationInfo> for request outputs
+template <typename RelocationInfoType>
+class RelocationTracker {
+  public:
+    static nn::GeneralResult<std::unique_ptr<RelocationTracker>> create(
+            std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory) {
+        auto mapping = NN_TRY(map(memory));
+        return std::make_unique<RelocationTracker<RelocationInfoType>>(
+                std::move(relocationInfos), std::move(memory), std::move(mapping));
+    }
+
+    RelocationTracker(std::vector<RelocationInfoType> relocationInfos, nn::SharedMemory memory,
+                      nn::Mapping mapping)
+        : kRelocationInfos(std::move(relocationInfos)),
+          kMemory(std::move(memory)),
+          kMapping(std::move(mapping)) {}
+
+    // Specializations defined in CommonUtils.cpp.
+    // For InputRelocationTracker, this method will copy pointer data to the shared memory pool.
+    // For OutputRelocationTracker, this method will copy shared memory data to the pointers.
+    void flush() const;
+
+  private:
+    const std::vector<RelocationInfoType> kRelocationInfos;
+    const nn::SharedMemory kMemory;
+    const nn::Mapping kMapping;
+};
+using InputRelocationTracker = RelocationTracker<InputRelocationInfo>;
+using OutputRelocationTracker = RelocationTracker<OutputRelocationInfo>;
+
+struct RequestRelocation {
+    std::unique_ptr<InputRelocationTracker> input;
+    std::unique_ptr<OutputRelocationTracker> output;
+};
+
+// Relocate pointer-based data to shared memory. If `request` has no
+// Request::Argument::LifeTime::POINTER data, the function returns with a reference to `request`. If
+// `request` has Request::Argument::LifeTime::POINTER data, the request is copied to
+// `maybeRequestInSharedOut` with the POINTER data relocated to a memory pool, and the function
+// returns with a reference to `*maybeRequestInSharedOut`. The `relocationOut` will be set to track
+// the input and output relocations.
+//
+// Unlike `flushDataFromPointerToShared`, this method will not copy the input pointer data to the
+// shared memory pool. Use `relocationOut` to flush the input or output data after the call.
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+        const nn::Request* request, uint32_t alignment, uint32_t padding,
+        std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut);
+
+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);
+
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle);
+nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle);
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& fences);
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_COMMON_UTILS_H
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..209b663
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/HandleError.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_COMMON_HANDLE_ERROR_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
+
+#include <android/hidl/base/1.0/IBase.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <type_traits>
+
+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();
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return static_cast<Type>(ret);
+    } else {
+        return {};
+    }
+}
+
+#define HANDLE_TRANSPORT_FAILURE(ret)                                                        \
+    ({                                                                                       \
+        auto result = ::android::hardware::neuralnetworks::utils::handleTransportError(ret); \
+        if (!result.has_value()) {                                                           \
+            return NN_ERROR(result.error().code) << result.error().message;                  \
+        }                                                                                    \
+        std::move(result).value();                                                           \
+    })
+
+template <typename Type>
+nn::GeneralResult<Type> makeGeneralFailure(
+        nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
+    if (!result.has_value()) {
+        return nn::error(status) << std::move(result).error();
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        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;
+    }
+    if constexpr (!std::is_same_v<Type, void>) {
+        return std::move(result).value();
+    } else {
+        return {};
+    }
+}
+
+template <typename Type>
+nn::ExecutionResult<Type> makeExecutionFailure(
+        nn::Result<Type> result, nn::ErrorStatus status = nn::ErrorStatus::GENERAL_FAILURE) {
+    return makeExecutionFailure(makeGeneralFailure(result, status));
+}
+
+#define HANDLE_HAL_STATUS(status)                                       \
+    if (const auto canonical = ::android::nn::convert(status).value_or( \
+                ::android::nn::ErrorStatus::GENERAL_FAILURE);           \
+        canonical == ::android::nn::ErrorStatus::NONE) {                \
+    } else                                                              \
+        return NN_ERROR(canonical)
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_HANDLE_ERROR_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
new file mode 100644
index 0000000..0e98c2e
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBuffer.h
@@ -0,0 +1,42 @@
+/*
+ * 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_INVALID_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidBuffer final : public nn::IBuffer {
+  public:
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& src,
+                                     const nn::Dimensions& dimensions) const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BUFFER_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.h
new file mode 100644
index 0000000..e86edda
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidBurst.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_COMMON_INVALID_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BURST_H
+
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidBurst final : public nn::IBurst {
+  public:
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_BURST_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
new file mode 100644
index 0000000..5e62b9a
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidDevice.h
@@ -0,0 +1,80 @@
+/*
+ * 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_INVALID_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidDevice final : public nn::IDevice {
+  public:
+    InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+                  nn::DeviceType type, std::vector<nn::Extension> extensions,
+                  nn::Capabilities capabilities,
+                  std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    const std::string kName;
+    const std::string kVersionString;
+    const nn::Version kFeatureLevel;
+    const nn::DeviceType kType;
+    const std::vector<nn::Extension> kExtensions;
+    const nn::Capabilities kCapabilities;
+    const std::pair<uint32_t, uint32_t> kNumberOfCacheFilesNeeded;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h
new file mode 100644
index 0000000..5b00221
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidExecution.h
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidExecution final : public nn::IExecution {
+  public:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_EXECUTION_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
new file mode 100644
index 0000000..de30aae
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/InvalidPreparedModel.h
@@ -0,0 +1,54 @@
+/*
+ * 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_INVALID_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class InvalidPreparedModel final : public nn::IPreparedModel {
+  public:
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_INVALID_PREPARED_MODEL_H
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..05110bc
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ProtectCallback.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.
+ */
+
+#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>
+
+// 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::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 bool mIsDeadObject GUARDED_BY(mMutex) = false;
+    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()>;
+    using Hold = base::ScopeGuard<Cleanup>;
+
+    // Precondition: `killable` must be non-null.
+    // `killable` must outlive the return value `Hold`.
+    [[nodiscard]] Hold protectCallback(IProtectedCallback* killable) const;
+
+    // Precondition: `killable` must be non-null.
+    // `killable` must outlive the `DeathHandler`.
+    void protectCallbackForLifetimeOfDeathHandler(IProtectedCallback* killable) const;
+
+  private:
+    DeathHandler(sp<hidl::base::V1_0::IBase> object, sp<DeathRecipient> deathRecipient);
+
+    sp<hidl::base::V1_0::IBase> mObject;
+    sp<DeathRecipient> mDeathRecipient;
+};
+
+}  // 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..c8ca6f2
--- /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>()>;
+
+    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::GeneralResult<nn::SharedBuffer> recover(const nn::IBuffer* failingBuffer) const;
+
+    nn::Request::MemoryDomainToken getToken() const override;
+
+    nn::GeneralResult<void> copyTo(const nn::SharedMemory& dst) const override;
+
+    nn::GeneralResult<void> copyFrom(const nn::SharedMemory& 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/ResilientBurst.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
new file mode 100644
index 0000000..fde2486
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientBurst.h
@@ -0,0 +1,71 @@
+/*
+ * 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_BURST_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BURST_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+class ResilientBurst final : public nn::IBurst,
+                             public std::enable_shared_from_this<ResilientBurst> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedBurst>()>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientBurst>> create(Factory makeBurst);
+
+    ResilientBurst(PrivateConstructorTag tag, Factory makeBurst, nn::SharedBurst burst);
+
+    nn::SharedBurst getBurst() const;
+    nn::GeneralResult<nn::SharedBurst> recover(const nn::IBurst* failingBurst) const;
+
+    OptionalCacheHold cacheMemory(const nn::SharedMemory& memory) const override;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> execute(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+  private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const;
+
+    const Factory kMakeBurst;
+    mutable std::mutex mMutex;
+    mutable nn::SharedBurst mBurst GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_BURST_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..84ae799
--- /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 EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedDevice> recover(const nn::IDevice* failingDevice,
+                                                bool blocking) const EXCLUDES(mMutex);
+
+    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::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache,
+            const nn::CacheToken& token) const override;
+
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCache(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& 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:
+    bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelInternal(
+            const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedPreparedModel> prepareModelFromCacheInternal(
+            nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+            const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const;
+    nn::GeneralResult<nn::SharedBuffer> allocateInternal(
+            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);
+    mutable bool mIsValid GUARDED_BY(mMutex) = true;
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_DEVICE_H
diff --git a/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
new file mode 100644
index 0000000..d0084e8
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientExecution.h
@@ -0,0 +1,66 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_H
+
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.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 ResilientExecution final : public nn::IExecution,
+                                 public std::enable_shared_from_this<ResilientExecution> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedExecution>()>;
+
+    static nn::GeneralResult<std::shared_ptr<const ResilientExecution>> create(
+            Factory makeExecution);
+
+    ResilientExecution(PrivateConstructorTag tag, Factory makeExecution,
+                       nn::SharedExecution execution);
+
+    nn::SharedExecution getExecution() const;
+    nn::GeneralResult<nn::SharedExecution> recover(const nn::IExecution* failingExecution) const;
+
+    nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> compute(
+            const nn::OptionalTimePoint& deadline) const override;
+
+    nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>> computeFenced(
+            const std::vector<nn::SyncFence>& waitFor, const nn::OptionalTimePoint& deadline,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+  private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+
+    const Factory kMakeExecution;
+    mutable std::mutex mMutex;
+    mutable nn::SharedExecution mExecution GUARDED_BY(mMutex);
+};
+
+}  // namespace android::hardware::neuralnetworks::utils
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_RESILIENT_EXECUTION_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..86533ed
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/ResilientPreparedModel.h
@@ -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.
+ */
+
+#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,
+                                     public std::enable_shared_from_this<ResilientPreparedModel> {
+    struct PrivateConstructorTag {};
+
+  public:
+    using Factory = std::function<nn::GeneralResult<nn::SharedPreparedModel>()>;
+
+    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::GeneralResult<nn::SharedPreparedModel> recover(
+            const nn::IPreparedModel* failingPreparedModel) 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::OptionalDuration& 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::OptionalDuration& loopTimeoutDuration,
+            const nn::OptionalDuration& timeoutDurationAfterFence) const override;
+
+    nn::GeneralResult<nn::SharedExecution> createReusableExecution(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const override;
+
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurst() const override;
+
+    std::any getUnderlyingResource() const override;
+
+  private:
+    bool isValidInternal() const EXCLUDES(mMutex);
+    nn::GeneralResult<nn::SharedExecution> createReusableExecutionInternal(
+            const nn::Request& request, nn::MeasureTiming measure,
+            const nn::OptionalDuration& loopTimeoutDuration) const;
+    nn::GeneralResult<nn::SharedBurst> configureExecutionBurstInternal() const;
+
+    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..6679afe
--- /dev/null
+++ b/neuralnetworks/utils/common/include/nnapi/hal/TransferValue.h
@@ -0,0 +1,124 @@
+/*
+ * 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/logging.h>
+#include <android-base/thread_annotations.h>
+
+#include <condition_variable>
+#include <functional>
+#include <mutex>
+#include <optional>
+#include <type_traits>
+
+namespace android::hardware::neuralnetworks::utils {
+
+// This class adapts a function pointer and offers two affordances:
+// 1) This class object can be used to generate a callback (via the implicit conversion operator)
+//    that can be used to send the result to `CallbackValue` when called.
+// 2) This class object can be used to retrieve the result of the callback with `take`.
+//
+// This class is thread compatible.
+template <typename ReturnType, typename... ArgTypes>
+class CallbackValue final {
+  public:
+    using FunctionType = std::add_pointer_t<ReturnType(ArgTypes...)>;
+    using CallbackType = std::function<void(ArgTypes...)>;
+
+    explicit CallbackValue(FunctionType fn);
+
+    // Creates a callback that forwards its arguments to `mFunction` and stores the result in
+    // `mReturnValue`.
+    /*implicit*/ operator CallbackType();  // NOLINT(google-explicit-constructor)
+
+    // Take the result of calling `mFunction`.
+    // Precondition: mReturnValue.has_value()
+    // Postcondition: !mReturnValue.has_value()
+    [[nodiscard]] ReturnType take();
+
+  private:
+    std::optional<ReturnType> mReturnValue;
+    FunctionType mFunction;
+};
+
+// Deduction guidelines for CallbackValue when constructed with a function pointer.
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue(ReturnType (*)(ArgTypes...))->CallbackValue<ReturnType, ArgTypes...>;
+
+// Thread-safe container to pass a value between threads.
+template <typename Type>
+class TransferValue final {
+  public:
+    // Put the value in `TransferValue`. If `TransferValue` already has a value, this function is a
+    // no-op.
+    void put(Type object) const;
+
+    // Take the value stored in `TransferValue`. If no value is available, this function will block
+    // until the value becomes available.
+    // Postcondition: !mObject.has_value()
+    [[nodiscard]] Type take() const;
+
+  private:
+    mutable std::mutex mMutex;
+    mutable std::condition_variable mCondition;
+    mutable std::optional<Type> mObject GUARDED_BY(mMutex);
+};
+
+// template implementations
+
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue<ReturnType, ArgTypes...>::CallbackValue(FunctionType fn) : mFunction(fn) {}
+
+template <typename ReturnType, typename... ArgTypes>
+CallbackValue<ReturnType, ArgTypes...>::operator CallbackType() {
+    return [this](ArgTypes... args) { mReturnValue = mFunction(args...); };
+}
+
+template <typename ReturnType, typename... ArgTypes>
+ReturnType CallbackValue<ReturnType, ArgTypes...>::take() {
+    CHECK(mReturnValue.has_value());
+    std::optional<ReturnType> object;
+    std::swap(object, mReturnValue);
+    return std::move(object).value();
+}
+
+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(); });
+    CHECK(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
new file mode 100644
index 0000000..906b0cf
--- /dev/null
+++ b/neuralnetworks/utils/common/src/CommonUtils.cpp
@@ -0,0 +1,457 @@
+/*
+ * 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 "CommonUtils.h"
+
+#include "HandleError.h"
+
+#include <android-base/logging.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware_buffer.h>
+#include <hidl/HidlSupport.h>
+#include <nnapi/Result.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/Validation.h>
+#include <vndk/hardware_buffer.h>
+
+#include <algorithm>
+#include <any>
+#include <functional>
+#include <optional>
+#include <variant>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+bool hasNoPointerData(const nn::Operand& operand);
+bool hasNoPointerData(const nn::Model::Subgraph& subgraph);
+bool hasNoPointerData(const nn::Request::Argument& argument);
+
+template <typename Type>
+bool hasNoPointerData(const std::vector<Type>& objects) {
+    return std::all_of(objects.begin(), objects.end(),
+                       [](const auto& object) { return hasNoPointerData(object); });
+}
+
+bool hasNoPointerData(const nn::DataLocation& location) {
+    return std::visit([](auto ptr) { return ptr == nullptr; }, location.pointer);
+}
+
+bool hasNoPointerData(const nn::Operand& operand) {
+    return hasNoPointerData(operand.location);
+}
+
+bool hasNoPointerData(const nn::Model::Subgraph& subgraph) {
+    return hasNoPointerData(subgraph.operands);
+}
+
+bool hasNoPointerData(const nn::Request::Argument& argument) {
+    return hasNoPointerData(argument.location);
+}
+
+void copyPointersToSharedMemory(nn::Operand* operand, nn::ConstantMemoryBuilder* memoryBuilder) {
+    CHECK(operand != nullptr);
+    CHECK(memoryBuilder != nullptr);
+
+    if (operand->lifetime != nn::Operand::LifeTime::POINTER) {
+        return;
+    }
+
+    const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
+                                  operand->location.pointer);
+    CHECK(data != nullptr);
+    operand->lifetime = nn::Operand::LifeTime::CONSTANT_REFERENCE;
+    operand->location = memoryBuilder->append(data, operand->location.length);
+}
+
+void copyPointersToSharedMemory(nn::Model::Subgraph* subgraph,
+                                nn::ConstantMemoryBuilder* memoryBuilder) {
+    CHECK(subgraph != nullptr);
+    std::for_each(subgraph->operands.begin(), subgraph->operands.end(),
+                  [memoryBuilder](auto& operand) {
+                      copyPointersToSharedMemory(&operand, memoryBuilder);
+                  });
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(std::vector<base::unique_fd> fds,
+                                                      const std::vector<int32_t>& ints) {
+    constexpr size_t kIntMax = std::numeric_limits<int>::max();
+    CHECK_LE(fds.size(), kIntMax);
+    CHECK_LE(ints.size(), kIntMax);
+    native_handle_t* nativeHandle =
+            native_handle_create(static_cast<int>(fds.size()), static_cast<int>(ints.size()));
+    if (nativeHandle == nullptr) {
+        return NN_ERROR() << "Failed to create native_handle";
+    }
+
+    for (size_t i = 0; i < fds.size(); ++i) {
+        nativeHandle->data[i] = fds[i].release();
+    }
+    std::copy(ints.begin(), ints.end(), nativeHandle->data + nativeHandle->numFds);
+
+    hidl_handle handle;
+    handle.setTo(nativeHandle, /*shouldOwn=*/true);
+    return handle;
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(base::unique_fd fd,
+                                                      const std::vector<int32_t>& ints) {
+    std::vector<base::unique_fd> fds;
+    fds.push_back(std::move(fd));
+    return createNativeHandleFrom(std::move(fds), ints);
+}
+
+nn::GeneralResult<hidl_handle> createNativeHandleFrom(const nn::Memory::Unknown::Handle& handle) {
+    std::vector<base::unique_fd> fds = NN_TRY(nn::dupFds(handle.fds.begin(), handle.fds.end()));
+    return createNativeHandleFrom(std::move(fds), handle.ints);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Ashmem& memory) {
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), {}));
+    return hidl_memory("ashmem", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Fd& memory) {
+    auto fd = NN_TRY(nn::dupFd(memory.fd));
+
+    const auto [lowOffsetBits, highOffsetBits] = nn::getIntsFromOffset(memory.offset);
+    const std::vector<int> ints = {memory.prot, lowOffsetBits, highOffsetBits};
+
+    auto handle = NN_TRY(createNativeHandleFrom(std::move(fd), ints));
+    return hidl_memory("mmap_fd", std::move(handle), memory.size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::HardwareBuffer& memory) {
+    const auto* ahwb = memory.handle.get();
+    AHardwareBuffer_Desc bufferDesc;
+    AHardwareBuffer_describe(ahwb, &bufferDesc);
+
+    const bool isBlob = bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB;
+    const size_t size = isBlob ? bufferDesc.width : 0;
+    const char* const name = isBlob ? "hardware_buffer_blob" : "hardware_buffer";
+
+    const native_handle_t* nativeHandle = AHardwareBuffer_getNativeHandle(ahwb);
+    const hidl_handle hidlHandle(nativeHandle);
+    hidl_handle copiedHandle(hidlHandle);
+
+    return hidl_memory(name, std::move(copiedHandle), size);
+}
+
+nn::GeneralResult<hidl_memory> createHidlMemoryFrom(const nn::Memory::Unknown& memory) {
+    return hidl_memory(memory.name, NN_TRY(createNativeHandleFrom(memory.handle)), memory.size);
+}
+
+nn::GeneralResult<nn::Memory::Unknown::Handle> unknownHandleFromNativeHandle(
+        const native_handle_t* handle) {
+    if (handle == nullptr) {
+        return NN_ERROR() << "unknownHandleFromNativeHandle failed because handle is nullptr";
+    }
+
+    std::vector<base::unique_fd> fds =
+            NN_TRY(nn::dupFds(handle->data + 0, handle->data + handle->numFds));
+
+    std::vector<int> ints(handle->data + handle->numFds,
+                          handle->data + handle->numFds + handle->numInts);
+
+    return nn::Memory::Unknown::Handle{.fds = std::move(fds), .ints = std::move(ints)};
+}
+
+}  // anonymous namespace
+
+nn::Capabilities::OperandPerformanceTable makeQuantized8PerformanceConsistentWithP(
+        const nn::Capabilities::PerformanceInfo& float32Performance,
+        const nn::Capabilities::PerformanceInfo& quantized8Performance) {
+    // In Android P, most data types are treated as having the same performance as
+    // TENSOR_QUANT8_ASYMM. This collection must be in sorted order.
+    std::vector<nn::Capabilities::OperandPerformance> operandPerformances = {
+            {.type = nn::OperandType::FLOAT32, .info = float32Performance},
+            {.type = nn::OperandType::INT32, .info = quantized8Performance},
+            {.type = nn::OperandType::UINT32, .info = quantized8Performance},
+            {.type = nn::OperandType::TENSOR_FLOAT32, .info = float32Performance},
+            {.type = nn::OperandType::TENSOR_INT32, .info = quantized8Performance},
+            {.type = nn::OperandType::TENSOR_QUANT8_ASYMM, .info = quantized8Performance},
+            {.type = nn::OperandType::OEM, .info = quantized8Performance},
+            {.type = nn::OperandType::TENSOR_OEM_BYTE, .info = quantized8Performance},
+    };
+    return nn::Capabilities::OperandPerformanceTable::create(std::move(operandPerformances))
+            .value();
+}
+
+bool hasNoPointerData(const nn::Model& model) {
+    return hasNoPointerData(model.main) && hasNoPointerData(model.referenced);
+}
+
+bool hasNoPointerData(const nn::Request& request) {
+    return hasNoPointerData(request.inputs) && hasNoPointerData(request.outputs);
+}
+
+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);
+    std::for_each(modelInShared.referenced.begin(), modelInShared.referenced.end(),
+                  [&memoryBuilder](auto& subgraph) {
+                      copyPointersToSharedMemory(&subgraph, &memoryBuilder);
+                  });
+
+    if (!memoryBuilder.empty()) {
+        auto memory = NN_TRY(memoryBuilder.finish());
+        modelInShared.pools.push_back(std::move(memory));
+    }
+
+    *maybeModelInSharedOut = modelInShared;
+    return **maybeModelInSharedOut;
+}
+
+template <>
+void InputRelocationTracker::flush() const {
+    // Copy from pointers to shared memory.
+    uint8_t* memoryPtr = static_cast<uint8_t*>(std::get<void*>(kMapping.pointer));
+    for (const auto& [data, length, offset] : kRelocationInfos) {
+        std::memcpy(memoryPtr + offset, data, length);
+    }
+}
+
+template <>
+void OutputRelocationTracker::flush() const {
+    // Copy from shared memory to pointers.
+    const uint8_t* memoryPtr = static_cast<const uint8_t*>(
+            std::visit([](auto ptr) { return static_cast<const void*>(ptr); }, kMapping.pointer));
+    for (const auto& [data, length, offset] : kRelocationInfos) {
+        std::memcpy(data, memoryPtr + offset, length);
+    }
+}
+
+nn::GeneralResult<std::reference_wrapper<const nn::Request>> convertRequestFromPointerToShared(
+        const nn::Request* request, uint32_t alignment, uint32_t padding,
+        std::optional<nn::Request>* maybeRequestInSharedOut, RequestRelocation* relocationOut) {
+    CHECK(request != nullptr);
+    CHECK(maybeRequestInSharedOut != nullptr);
+    CHECK(relocationOut != 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;
+
+    RequestRelocation relocation;
+
+    // Change input pointers to shared memory.
+    nn::MutableMemoryBuilder inputBuilder(requestInShared.pools.size());
+    std::vector<InputRelocationInfo> inputRelocationInfos;
+    for (auto& input : requestInShared.inputs) {
+        const auto& location = input.location;
+        if (input.lifetime != nn::Request::Argument::LifeTime::POINTER) {
+            continue;
+        }
+
+        input.lifetime = nn::Request::Argument::LifeTime::POOL;
+        const void* data = std::visit([](auto ptr) { return static_cast<const void*>(ptr); },
+                                      location.pointer);
+        CHECK(data != nullptr);
+        input.location = inputBuilder.append(location.length, alignment, padding);
+        inputRelocationInfos.push_back({data, input.location.length, input.location.offset});
+    }
+
+    // Allocate input memory.
+    if (!inputBuilder.empty()) {
+        auto memory = NN_TRY(inputBuilder.finish());
+        requestInShared.pools.push_back(memory);
+        relocation.input = NN_TRY(
+                InputRelocationTracker::create(std::move(inputRelocationInfos), std::move(memory)));
+    }
+
+    // Change output pointers to shared memory.
+    nn::MutableMemoryBuilder outputBuilder(requestInShared.pools.size());
+    std::vector<OutputRelocationInfo> outputRelocationInfos;
+    for (auto& output : requestInShared.outputs) {
+        const auto& location = output.location;
+        if (output.lifetime != nn::Request::Argument::LifeTime::POINTER) {
+            continue;
+        }
+
+        output.lifetime = nn::Request::Argument::LifeTime::POOL;
+        void* data = std::get<void*>(location.pointer);
+        CHECK(data != nullptr);
+        output.location = outputBuilder.append(location.length, alignment, padding);
+        outputRelocationInfos.push_back({data, output.location.length, output.location.offset});
+    }
+
+    // Allocate output memory.
+    if (!outputBuilder.empty()) {
+        auto memory = NN_TRY(outputBuilder.finish());
+        requestInShared.pools.push_back(memory);
+        relocation.output = NN_TRY(OutputRelocationTracker::create(std::move(outputRelocationInfos),
+                                                                   std::move(memory)));
+    }
+
+    *maybeRequestInSharedOut = requestInShared;
+    *relocationOut = std::move(relocation);
+    return **maybeRequestInSharedOut;
+}
+
+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) {
+    if (memory == nullptr) {
+        return NN_ERROR() << "Memory must be non-empty";
+    }
+    return std::visit([](const auto& x) { return createHidlMemoryFrom(x); }, memory->handle);
+}
+
+static uint32_t roundUpToMultiple(uint32_t value, uint32_t multiple) {
+    return (value + multiple - 1) / multiple * multiple;
+}
+
+nn::GeneralResult<nn::SharedMemory> createSharedMemoryFromHidlMemory(const hidl_memory& memory) {
+    CHECK_LE(memory.size(), std::numeric_limits<size_t>::max());
+    if (!memory.valid()) {
+        return NN_ERROR() << "Unable to convert invalid hidl_memory";
+    }
+
+    if (memory.name() == "ashmem") {
+        if (memory.handle()->numFds != 1) {
+            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+                              << memory.handle()->numFds << " numFds, but expected 1";
+        }
+        if (memory.handle()->numInts != 0) {
+            return NN_ERROR() << "Unable to convert invalid ashmem memory object with "
+                              << memory.handle()->numInts << " numInts, but expected 0";
+        }
+        auto handle = nn::Memory::Ashmem{
+                .fd = NN_TRY(nn::dupFd(memory.handle()->data[0])),
+                .size = static_cast<size_t>(memory.size()),
+        };
+        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+    }
+
+    if (memory.name() == "mmap_fd") {
+        if (memory.handle()->numFds != 1) {
+            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+                              << memory.handle()->numFds << " numFds, but expected 1";
+        }
+        if (memory.handle()->numInts != 3) {
+            return NN_ERROR() << "Unable to convert invalid mmap_fd memory object with "
+                              << memory.handle()->numInts << " numInts, but expected 3";
+        }
+
+        const int fd = memory.handle()->data[0];
+        const int prot = memory.handle()->data[1];
+        const int lower = memory.handle()->data[2];
+        const int higher = memory.handle()->data[3];
+        const size_t offset = nn::getOffsetFromInts(lower, higher);
+
+        return nn::createSharedMemoryFromFd(static_cast<size_t>(memory.size()), prot, fd, offset);
+    }
+
+    if (memory.name() != "hardware_buffer_blob") {
+        auto handle = nn::Memory::Unknown{
+                .handle = NN_TRY(unknownHandleFromNativeHandle(memory.handle())),
+                .size = static_cast<size_t>(memory.size()),
+                .name = memory.name(),
+        };
+        return std::make_shared<const nn::Memory>(nn::Memory{.handle = std::move(handle)});
+    }
+
+    const auto size = memory.size();
+    const auto format = AHARDWAREBUFFER_FORMAT_BLOB;
+    const auto usage = AHARDWAREBUFFER_USAGE_CPU_READ_OFTEN | AHARDWAREBUFFER_USAGE_CPU_WRITE_OFTEN;
+    const uint32_t width = size;
+    const uint32_t height = 1;  // height is always 1 for BLOB mode AHardwareBuffer.
+    const uint32_t layers = 1;  // layers is always 1 for BLOB mode AHardwareBuffer.
+
+    // AHardwareBuffer_createFromHandle() might fail because an allocator
+    // expects a specific stride value. In that case, we try to guess it by
+    // aligning the width to small powers of 2.
+    // TODO(b/174120849): Avoid stride assumptions.
+    AHardwareBuffer* hardwareBuffer = nullptr;
+    status_t status = UNKNOWN_ERROR;
+    for (uint32_t alignment : {1, 4, 32, 64, 128, 2, 8, 16}) {
+        const uint32_t stride = roundUpToMultiple(width, alignment);
+        AHardwareBuffer_Desc desc{
+                .width = width,
+                .height = height,
+                .layers = layers,
+                .format = format,
+                .usage = usage,
+                .stride = stride,
+        };
+        status = AHardwareBuffer_createFromHandle(&desc, memory.handle(),
+                                                  AHARDWAREBUFFER_CREATE_FROM_HANDLE_METHOD_CLONE,
+                                                  &hardwareBuffer);
+        if (status == NO_ERROR) {
+            break;
+        }
+    }
+    if (status != NO_ERROR) {
+        return NN_ERROR(nn::ErrorStatus::GENERAL_FAILURE)
+               << "Can't create AHardwareBuffer from handle. Error: " << status;
+    }
+
+    return nn::createSharedMemoryFromAHWB(hardwareBuffer, /*takeOwnership=*/true);
+}
+
+nn::GeneralResult<hidl_handle> hidlHandleFromSharedHandle(const nn::Handle& handle) {
+    base::unique_fd fd = NN_TRY(nn::dupFd(handle.get()));
+    return createNativeHandleFrom(std::move(fd), {});
+}
+
+nn::GeneralResult<nn::Handle> sharedHandleFromNativeHandle(const native_handle_t* handle) {
+    if (handle == nullptr) {
+        return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle is nullptr";
+    }
+    if (handle->numFds != 1 || handle->numInts != 0) {
+        return NN_ERROR() << "sharedHandleFromNativeHandle failed because handle does not only "
+                             "hold a single fd";
+    }
+    return nn::dupFd(handle->data[0]);
+}
+
+nn::GeneralResult<hidl_vec<hidl_handle>> convertSyncFences(
+        const std::vector<nn::SyncFence>& syncFences) {
+    hidl_vec<hidl_handle> handles(syncFences.size());
+    for (size_t i = 0; i < syncFences.size(); ++i) {
+        const auto& handle = syncFences[i].getSharedHandle();
+        if (handle == nullptr) {
+            return NN_ERROR() << "convertSyncFences failed because sync fence is empty";
+        }
+        handles[i] = NN_TRY(hidlHandleFromSharedHandle(*handle));
+    }
+    return handles;
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidBuffer.cpp b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
new file mode 100644
index 0000000..e73001d
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidBuffer.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 "InvalidBuffer.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::Request::MemoryDomainToken InvalidBuffer::getToken() const {
+    return nn::Request::MemoryDomainToken{};
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyTo(const nn::SharedMemory& /*dst*/) const {
+    return NN_ERROR() << "InvalidBuffer";
+}
+
+nn::GeneralResult<void> InvalidBuffer::copyFrom(const nn::SharedMemory& /*src*/,
+                                                const nn::Dimensions& /*dimensions*/) const {
+    return NN_ERROR() << "InvalidBuffer";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidBurst.cpp b/neuralnetworks/utils/common/src/InvalidBurst.cpp
new file mode 100644
index 0000000..0191533
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidBurst.cpp
@@ -0,0 +1,47 @@
+/*
+ * 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 "InvalidBurst.h"
+
+#include <nnapi/IBurst.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <optional>
+#include <utility>
+
+namespace android::hardware::neuralnetworks::utils {
+
+InvalidBurst::OptionalCacheHold InvalidBurst::cacheMemory(
+        const nn::SharedMemory& /*memory*/) const {
+    return nullptr;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidBurst::execute(
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidBurst";
+}
+
+nn::GeneralResult<nn::SharedExecution> InvalidBurst::createReusableExecution(
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidBurst";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidDevice.cpp b/neuralnetworks/utils/common/src/InvalidDevice.cpp
new file mode 100644
index 0000000..535ccb4
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidDevice.cpp
@@ -0,0 +1,105 @@
+/*
+ * 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 "InvalidDevice.h"
+
+#include "InvalidBuffer.h"
+#include "InvalidPreparedModel.h"
+
+#include <nnapi/IBuffer.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+InvalidDevice::InvalidDevice(std::string name, std::string versionString, nn::Version featureLevel,
+                             nn::DeviceType type, std::vector<nn::Extension> extensions,
+                             nn::Capabilities capabilities,
+                             std::pair<uint32_t, uint32_t> numberOfCacheFilesNeeded)
+    : kName(std::move(name)),
+      kVersionString(std::move(versionString)),
+      kFeatureLevel(featureLevel),
+      kType(type),
+      kExtensions(std::move(extensions)),
+      kCapabilities(std::move(capabilities)),
+      kNumberOfCacheFilesNeeded(numberOfCacheFilesNeeded) {}
+
+const std::string& InvalidDevice::getName() const {
+    return kName;
+}
+
+const std::string& InvalidDevice::getVersionString() const {
+    return kVersionString;
+}
+
+nn::Version InvalidDevice::getFeatureLevel() const {
+    return kFeatureLevel;
+}
+
+nn::DeviceType InvalidDevice::getType() const {
+    return kType;
+}
+
+const std::vector<nn::Extension>& InvalidDevice::getSupportedExtensions() const {
+    return kExtensions;
+}
+
+const nn::Capabilities& InvalidDevice::getCapabilities() const {
+    return kCapabilities;
+}
+
+std::pair<uint32_t, uint32_t> InvalidDevice::getNumberOfCacheFilesNeeded() const {
+    return kNumberOfCacheFilesNeeded;
+}
+
+nn::GeneralResult<void> InvalidDevice::wait() const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<std::vector<bool>> InvalidDevice::getSupportedOperations(
+        const nn::Model& /*model*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModel(
+        const nn::Model& /*model*/, nn::ExecutionPreference /*preference*/,
+        nn::Priority /*priority*/, nn::OptionalTimePoint /*deadline*/,
+        const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> InvalidDevice::prepareModelFromCache(
+        nn::OptionalTimePoint /*deadline*/, const std::vector<nn::SharedHandle>& /*modelCache*/,
+        const std::vector<nn::SharedHandle>& /*dataCache*/, const nn::CacheToken& /*token*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+nn::GeneralResult<nn::SharedBuffer> InvalidDevice::allocate(
+        const nn::BufferDesc& /*desc*/,
+        const std::vector<nn::SharedPreparedModel>& /*preparedModels*/,
+        const std::vector<nn::BufferRole>& /*inputRoles*/,
+        const std::vector<nn::BufferRole>& /*outputRoles*/) const {
+    return NN_ERROR() << "InvalidDevice";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidExecution.cpp b/neuralnetworks/utils/common/src/InvalidExecution.cpp
new file mode 100644
index 0000000..c4edd25
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidExecution.cpp
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+#include "InvalidExecution.h"
+
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> InvalidExecution::compute(
+        const nn::OptionalTimePoint& /*deadline*/) const {
+    return NN_ERROR() << "InvalidExecution";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+InvalidExecution::computeFenced(const std::vector<nn::SyncFence>& /*waitFor*/,
+                                const nn::OptionalTimePoint& /*deadline*/,
+                                const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR() << "InvalidExecution";
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
new file mode 100644
index 0000000..8195462
--- /dev/null
+++ b/neuralnetworks/utils/common/src/InvalidPreparedModel.cpp
@@ -0,0 +1,59 @@
+/*
+ * 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 "InvalidPreparedModel.h"
+
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/Types.h>
+
+#include <memory>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+InvalidPreparedModel::execute(const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+                              const nn::OptionalTimePoint& /*deadline*/,
+                              const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+InvalidPreparedModel::executeFenced(
+        const nn::Request& /*request*/, const std::vector<nn::SyncFence>& /*waitFor*/,
+        nn::MeasureTiming /*measure*/, const nn::OptionalTimePoint& /*deadline*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/,
+        const nn::OptionalDuration& /*timeoutDurationAfterFence*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+nn::GeneralResult<nn::SharedExecution> InvalidPreparedModel::createReusableExecution(
+        const nn::Request& /*request*/, nn::MeasureTiming /*measure*/,
+        const nn::OptionalDuration& /*loopTimeoutDuration*/) const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+nn::GeneralResult<nn::SharedBurst> InvalidPreparedModel::configureExecutionBurst() const {
+    return NN_ERROR() << "InvalidPreparedModel";
+}
+
+std::any InvalidPreparedModel::getUnderlyingResource() const {
+    return {};
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ProtectCallback.cpp b/neuralnetworks/utils/common/src/ProtectCallback.cpp
new file mode 100644
index 0000000..18e1f3b
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ProtectCallback.cpp
@@ -0,0 +1,103 @@
+/*
+ * 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(); });
+    mObjects.clear();
+    mIsDeadObject = true;
+}
+
+void DeathRecipient::add(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    if (mIsDeadObject) {
+        killable->notifyAsDeadObject();
+    } else {
+        mObjects.push_back(killable);
+    }
+}
+
+void DeathRecipient::remove(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    std::lock_guard guard(mMutex);
+    const auto newEnd = std::remove(mObjects.begin(), mObjects.end(), killable);
+    mObjects.erase(newEnd, mObjects.end());
+}
+
+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 = HANDLE_TRANSPORT_FAILURE(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)
+    : mObject(std::move(object)), mDeathRecipient(std::move(deathRecipient)) {
+    CHECK(mObject != nullptr);
+    CHECK(mDeathRecipient != nullptr);
+}
+
+DeathHandler::~DeathHandler() {
+    if (mObject != nullptr && mDeathRecipient != nullptr) {
+        const auto successful = mObject->unlinkToDeath(mDeathRecipient).isOk();
+        if (!successful) {
+            LOG(ERROR) << "IBase::linkToDeath failed";
+        }
+    }
+}
+
+[[nodiscard]] base::ScopeGuard<DeathHandler::Cleanup> DeathHandler::protectCallback(
+        IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    mDeathRecipient->add(killable);
+    return base::make_scope_guard(
+            [deathRecipient = mDeathRecipient, killable] { deathRecipient->remove(killable); });
+}
+
+void DeathHandler::protectCallbackForLifetimeOfDeathHandler(IProtectedCallback* killable) const {
+    CHECK(killable != nullptr);
+    mDeathRecipient->add(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..1904375
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientBuffer.cpp
@@ -0,0 +1,115 @@
+/*
+ * 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/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientBuffer& resilientBuffer, const FnType& fn)
+        -> decltype(fn(*resilientBuffer.getBuffer())) {
+    auto buffer = resilientBuffer.getBuffer();
+    auto result = fn(*buffer);
+
+    // Immediately return if device is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybeBuffer = resilientBuffer.recover(buffer.get());
+    if (!maybeBuffer.has_value()) {
+        const auto& [resultErrorMessage, resultErrorCode] = result.error();
+        const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeBuffer.error();
+        return nn::error(resultErrorCode)
+               << resultErrorMessage << ", and failed to recover dead buffer with error "
+               << recoveryErrorCode << ": " << recoveryErrorMessage;
+    }
+    buffer = std::move(maybeBuffer).value();
+
+    return fn(*buffer);
+}
+
+}  // namespace
+
+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());
+    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::GeneralResult<nn::SharedBuffer> ResilientBuffer::recover(
+        const nn::IBuffer* failingBuffer) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing prepared model.
+    if (mBuffer.get() != failingBuffer) {
+        return mBuffer;
+    }
+
+    mBuffer = NN_TRY(kMakeBuffer());
+    return mBuffer;
+}
+
+nn::Request::MemoryDomainToken ResilientBuffer::getToken() const {
+    return getBuffer()->getToken();
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyTo(const nn::SharedMemory& dst) const {
+    const auto fn = [&dst](const nn::IBuffer& buffer) { return buffer.copyTo(dst); };
+    return protect(*this, fn);
+}
+
+nn::GeneralResult<void> ResilientBuffer::copyFrom(const nn::SharedMemory& src,
+                                                  const nn::Dimensions& dimensions) const {
+    const auto fn = [&src, &dimensions](const nn::IBuffer& buffer) {
+        return buffer.copyFrom(src, dimensions);
+    };
+    return protect(*this, fn);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientBurst.cpp b/neuralnetworks/utils/common/src/ResilientBurst.cpp
new file mode 100644
index 0000000..79cbe39
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientBurst.cpp
@@ -0,0 +1,147 @@
+/*
+ * 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 "ResilientBurst.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IBurst.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <optional>
+#include <utility>
+
+#include "InvalidExecution.h"
+#include "ResilientExecution.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientBurst& resilientBurst, const FnType& fn)
+        -> decltype(fn(*resilientBurst.getBurst())) {
+    auto burst = resilientBurst.getBurst();
+    auto result = fn(*burst);
+
+    // Immediately return if burst is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybeBurst = resilientBurst.recover(burst.get());
+    if (!maybeBurst.has_value()) {
+        const auto& [message, code] = maybeBurst.error();
+        std::ostringstream oss;
+        oss << ", and failed to recover dead burst object with error " << code << ": " << message;
+        result.error().message += oss.str();
+        return result;
+    }
+    burst = std::move(maybeBurst).value();
+
+    return fn(*burst);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientBurst>> ResilientBurst::create(Factory makeBurst) {
+    if (makeBurst == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientBurst::create must have non-empty makeBurst";
+    }
+    auto burst = NN_TRY(makeBurst());
+    CHECK(burst != nullptr);
+    return std::make_shared<ResilientBurst>(PrivateConstructorTag{}, std::move(makeBurst),
+                                            std::move(burst));
+}
+
+ResilientBurst::ResilientBurst(PrivateConstructorTag /*tag*/, Factory makeBurst,
+                               nn::SharedBurst burst)
+    : kMakeBurst(std::move(makeBurst)), mBurst(std::move(burst)) {
+    CHECK(kMakeBurst != nullptr);
+    CHECK(mBurst != nullptr);
+}
+
+nn::SharedBurst ResilientBurst::getBurst() const {
+    std::lock_guard guard(mMutex);
+    return mBurst;
+}
+
+nn::GeneralResult<nn::SharedBurst> ResilientBurst::recover(const nn::IBurst* failingBurst) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing burst.
+    if (mBurst.get() != failingBurst) {
+        return mBurst;
+    }
+
+    mBurst = NN_TRY(kMakeBurst());
+    return mBurst;
+}
+
+ResilientBurst::OptionalCacheHold ResilientBurst::cacheMemory(
+        const nn::SharedMemory& memory) const {
+    return getBurst()->cacheMemory(memory);
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>> ResilientBurst::execute(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalTimePoint& deadline,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    const auto fn = [&request, measure, deadline, loopTimeoutDuration](const nn::IBurst& burst) {
+        return burst.execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientExecution::Factory makeExecution =
+            [burst = std::move(self), request, measure, loopTimeoutDuration] {
+        return burst->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+    };
+    return ResilientExecution::create(std::move(makeExecution));
+#else
+    return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientBurst::createReusableExecutionInternal(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidExecution>();
+    }
+    const auto fn = [&request, measure, &loopTimeoutDuration](const nn::IBurst& burst) {
+        return burst.createReusableExecution(request, measure, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
+bool ResilientBurst::isValidInternal() const {
+    return true;
+}
+
+}  // 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..2023c9a
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientDevice.cpp
@@ -0,0 +1,274 @@
+/*
+ * 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 "InvalidBuffer.h"
+#include "InvalidDevice.h"
+#include "InvalidPreparedModel.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;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybeDevice = resilientDevice.recover(device.get(), blocking);
+    if (!maybeDevice.has_value()) {
+        const auto& [resultErrorMessage, resultErrorCode] = result.error();
+        const auto& [recoveryErrorMessage, recoveryErrorCode] = maybeDevice.error();
+        return nn::error(resultErrorCode)
+               << resultErrorMessage << ", and failed to recover dead device with error "
+               << recoveryErrorCode << ": " << recoveryErrorMessage;
+    }
+    device = std::move(maybeDevice).value();
+
+    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::GeneralResult<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 device = NN_TRY(kMakeDevice(blocking));
+
+    // If recovered device has different metadata than what is cached (i.e., because it was
+    // updated), mark the device as invalid and preserve the cached data.
+    auto compare = [this, &device](auto fn) REQUIRES(mMutex) {
+        return std::invoke(fn, mDevice) != std::invoke(fn, device);
+    };
+    if (compare(&IDevice::getName) || compare(&IDevice::getVersionString) ||
+        compare(&IDevice::getFeatureLevel) || compare(&IDevice::getType) ||
+        compare(&IDevice::getSupportedExtensions) || compare(&IDevice::getCapabilities)) {
+        LOG(ERROR) << "Recovered device has different metadata than what is cached. Marking "
+                      "IDevice object as invalid.";
+        device = std::make_shared<const InvalidDevice>(
+                kName, kVersionString, mDevice->getFeatureLevel(), mDevice->getType(), kExtensions,
+                kCapabilities, mDevice->getNumberOfCacheFilesNeeded());
+        mIsValid = false;
+    }
+
+    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::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel = [device = std::move(self), model,
+                                                         preference, priority, deadline, modelCache,
+                                                         dataCache, token] {
+        return device->prepareModelInternal(model, preference, priority, deadline, modelCache,
+                                            dataCache, token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+#else
+    return prepareModelInternal(model, preference, priority, deadline, modelCache, dataCache,
+                                token);
+#endif
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCache(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientPreparedModel::Factory makePreparedModel = [device = std::move(self), deadline,
+                                                         modelCache, dataCache, token] {
+        return device->prepareModelFromCacheInternal(deadline, modelCache, dataCache, token);
+    };
+    return ResilientPreparedModel::create(std::move(makePreparedModel));
+#else
+    return prepareModelFromCacheInternal(deadline, modelCache, dataCache, token);
+#endif
+}
+
+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 {
+#if 0
+    auto self = shared_from_this();
+    ResilientBuffer::Factory makeBuffer = [device = std::move(self), desc, preparedModels,
+                                           inputRoles, outputRoles] {
+        return device->allocateInternal(desc, preparedModels, inputRoles, outputRoles);
+    };
+    return ResilientBuffer::create(std::move(makeBuffer));
+#else
+    return allocateInternal(desc, preparedModels, inputRoles, outputRoles);
+#endif
+}
+
+bool ResilientDevice::isValidInternal() const {
+    std::lock_guard hold(mMutex);
+    return mIsValid;
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelInternal(
+        const nn::Model& model, nn::ExecutionPreference preference, nn::Priority priority,
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidPreparedModel>();
+    }
+    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=*/false);
+}
+
+nn::GeneralResult<nn::SharedPreparedModel> ResilientDevice::prepareModelFromCacheInternal(
+        nn::OptionalTimePoint deadline, const std::vector<nn::SharedHandle>& modelCache,
+        const std::vector<nn::SharedHandle>& dataCache, const nn::CacheToken& token) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidPreparedModel>();
+    }
+    const auto fn = [&deadline, &modelCache, &dataCache, &token](const nn::IDevice& device) {
+        return device.prepareModelFromCache(deadline, modelCache, dataCache, token);
+    };
+    return protect(*this, fn, /*blocking=*/false);
+}
+
+nn::GeneralResult<nn::SharedBuffer> ResilientDevice::allocateInternal(
+        const nn::BufferDesc& desc, const std::vector<nn::SharedPreparedModel>& preparedModels,
+        const std::vector<nn::BufferRole>& inputRoles,
+        const std::vector<nn::BufferRole>& outputRoles) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidBuffer>();
+    }
+    const auto fn = [&desc, &preparedModels, &inputRoles, &outputRoles](const nn::IDevice& device) {
+        return device.allocate(desc, preparedModels, inputRoles, outputRoles);
+    };
+    return protect(*this, fn, /*blocking=*/false);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/src/ResilientExecution.cpp b/neuralnetworks/utils/common/src/ResilientExecution.cpp
new file mode 100644
index 0000000..46b404a
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientExecution.cpp
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+#include "ResilientExecution.h"
+
+#include "InvalidBurst.h"
+#include "ResilientBurst.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IExecution.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientExecution& resilientExecution, const FnType& fn)
+        -> decltype(fn(*resilientExecution.getExecution())) {
+    auto execution = resilientExecution.getExecution();
+    auto result = fn(*execution);
+
+    // Immediately return if prepared model is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybeExecution = resilientExecution.recover(execution.get());
+    if (!maybeExecution.has_value()) {
+        const auto& [message, code] = maybeExecution.error();
+        std::ostringstream oss;
+        oss << ", and failed to recover dead prepared model with error " << code << ": " << message;
+        result.error().message += oss.str();
+        return result;
+    }
+    execution = std::move(maybeExecution).value();
+
+    return fn(*execution);
+}
+
+}  // namespace
+
+nn::GeneralResult<std::shared_ptr<const ResilientExecution>> ResilientExecution::create(
+        Factory makeExecution) {
+    if (makeExecution == nullptr) {
+        return NN_ERROR(nn::ErrorStatus::INVALID_ARGUMENT)
+               << "utils::ResilientExecution::create must have non-empty makeExecution";
+    }
+    auto execution = NN_TRY(makeExecution());
+    CHECK(execution != nullptr);
+    return std::make_shared<ResilientExecution>(PrivateConstructorTag{}, std::move(makeExecution),
+                                                std::move(execution));
+}
+
+ResilientExecution::ResilientExecution(PrivateConstructorTag /*tag*/, Factory makeExecution,
+                                       nn::SharedExecution execution)
+    : kMakeExecution(std::move(makeExecution)), mExecution(std::move(execution)) {
+    CHECK(kMakeExecution != nullptr);
+    CHECK(mExecution != nullptr);
+}
+
+nn::SharedExecution ResilientExecution::getExecution() const {
+    std::lock_guard guard(mMutex);
+    return mExecution;
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientExecution::recover(
+        const nn::IExecution* failingExecution) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing prepared model.
+    if (mExecution.get() != failingExecution) {
+        return mExecution;
+    }
+
+    mExecution = NN_TRY(kMakeExecution());
+    return mExecution;
+}
+
+nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>
+ResilientExecution::compute(const nn::OptionalTimePoint& deadline) const {
+    const auto fn = [&deadline](const nn::IExecution& execution) {
+        return execution.compute(deadline);
+    };
+    return protect(*this, fn);
+}
+
+nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>
+ResilientExecution::computeFenced(const std::vector<nn::SyncFence>& waitFor,
+                                  const nn::OptionalTimePoint& deadline,
+                                  const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto fn = [&waitFor, &deadline,
+                     &timeoutDurationAfterFence](const nn::IExecution& execution) {
+        return execution.computeFenced(waitFor, deadline, timeoutDurationAfterFence);
+    };
+    return protect(*this, fn);
+}
+
+bool ResilientExecution::isValidInternal() const {
+    return true;
+}
+
+}  // 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..1ae19bc
--- /dev/null
+++ b/neuralnetworks/utils/common/src/ResilientPreparedModel.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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 "InvalidBurst.h"
+#include "InvalidExecution.h"
+#include "ResilientBurst.h"
+#include "ResilientExecution.h"
+
+#include <android-base/logging.h>
+#include <android-base/thread_annotations.h>
+#include <nnapi/IPreparedModel.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+
+#include <functional>
+#include <memory>
+#include <mutex>
+#include <sstream>
+#include <utility>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+template <typename FnType>
+auto protect(const ResilientPreparedModel& resilientPreparedModel, const FnType& fn)
+        -> decltype(fn(*resilientPreparedModel.getPreparedModel())) {
+    auto preparedModel = resilientPreparedModel.getPreparedModel();
+    auto result = fn(*preparedModel);
+
+    // Immediately return if prepared model is not dead.
+    if (result.has_value() || result.error().code != nn::ErrorStatus::DEAD_OBJECT) {
+        return result;
+    }
+
+    // Attempt recovery and return if it fails.
+    auto maybePreparedModel = resilientPreparedModel.recover(preparedModel.get());
+    if (!maybePreparedModel.has_value()) {
+        const auto& [message, code] = maybePreparedModel.error();
+        std::ostringstream oss;
+        oss << ", and failed to recover dead prepared model with error " << code << ": " << message;
+        result.error().message += oss.str();
+        return result;
+    }
+    preparedModel = std::move(maybePreparedModel).value();
+
+    return fn(*preparedModel);
+}
+
+}  // namespace
+
+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());
+    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::GeneralResult<nn::SharedPreparedModel> ResilientPreparedModel::recover(
+        const nn::IPreparedModel* failingPreparedModel) const {
+    std::lock_guard guard(mMutex);
+
+    // Another caller updated the failing prepared model.
+    if (mPreparedModel.get() != failingPreparedModel) {
+        return mPreparedModel;
+    }
+
+    mPreparedModel = NN_TRY(kMakePreparedModel());
+    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::OptionalDuration& loopTimeoutDuration) const {
+    const auto fn = [&request, measure, &deadline,
+                     &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) {
+        return preparedModel.execute(request, measure, deadline, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
+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::OptionalDuration& loopTimeoutDuration,
+                                      const nn::OptionalDuration& timeoutDurationAfterFence) const {
+    const auto fn = [&request, &waitFor, measure, &deadline, &loopTimeoutDuration,
+                     &timeoutDurationAfterFence](const nn::IPreparedModel& preparedModel) {
+        return preparedModel.executeFenced(request, waitFor, measure, deadline, loopTimeoutDuration,
+                                           timeoutDurationAfterFence);
+    };
+    return protect(*this, fn);
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecution(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+#if 0
+    auto self = shared_from_this();
+    ResilientExecution::Factory makeExecution =
+            [preparedModel = std::move(self), request, measure, loopTimeoutDuration] {
+        return preparedModel->createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+    };
+    return ResilientExecution::create(std::move(makeExecution));
+#else
+    return createReusableExecutionInternal(request, measure, loopTimeoutDuration);
+#endif
+}
+
+nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurst() const {
+#if 0
+    auto self = shared_from_this();
+    ResilientBurst::Factory makeBurst =
+            [preparedModel = std::move(self)]() -> nn::GeneralResult<nn::SharedBurst> {
+        return preparedModel->configureExecutionBurst();
+    };
+    return ResilientBurst::create(std::move(makeBurst));
+#else
+    return configureExecutionBurstInternal();
+#endif
+}
+
+nn::GeneralResult<nn::SharedExecution> ResilientPreparedModel::createReusableExecutionInternal(
+        const nn::Request& request, nn::MeasureTiming measure,
+        const nn::OptionalDuration& loopTimeoutDuration) const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidExecution>();
+    }
+    const auto fn = [&request, measure,
+                     &loopTimeoutDuration](const nn::IPreparedModel& preparedModel) {
+        return preparedModel.createReusableExecution(request, measure, loopTimeoutDuration);
+    };
+    return protect(*this, fn);
+}
+
+std::any ResilientPreparedModel::getUnderlyingResource() const {
+    return getPreparedModel()->getUnderlyingResource();
+}
+
+bool ResilientPreparedModel::isValidInternal() const {
+    return true;
+}
+
+nn::GeneralResult<nn::SharedBurst> ResilientPreparedModel::configureExecutionBurstInternal() const {
+    if (!isValidInternal()) {
+        return std::make_shared<const InvalidBurst>();
+    }
+    const auto fn = [](const nn::IPreparedModel& preparedModel) {
+        return preparedModel.configureExecutionBurst();
+    };
+    return protect(*this, fn);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/MockBuffer.h b/neuralnetworks/utils/common/test/MockBuffer.h
new file mode 100644
index 0000000..3599d0c
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockBuffer.h
@@ -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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IBuffer.h>
+#include <nnapi/Types.h>
+
+namespace android::nn {
+
+class MockBuffer final : public IBuffer {
+  public:
+    MOCK_METHOD(Request::MemoryDomainToken, getToken, (), (const, override));
+    MOCK_METHOD(GeneralResult<void>, copyTo, (const SharedMemory& dst), (const, override));
+    MOCK_METHOD(GeneralResult<void>, copyFrom,
+                (const SharedMemory& src, const Dimensions& dimensions), (const, override));
+};
+
+}  // namespace android::nn
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_BUFFER_H
diff --git a/neuralnetworks/utils/common/test/MockDevice.h b/neuralnetworks/utils/common/test/MockDevice.h
new file mode 100644
index 0000000..a9428bc
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockDevice.h
@@ -0,0 +1,57 @@
+/*
+ * 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_TEST_MOCK_DEVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IDevice.h>
+
+namespace android::nn {
+
+class MockDevice final : public IDevice {
+  public:
+    MOCK_METHOD(const std::string&, getName, (), (const, override));
+    MOCK_METHOD(const std::string&, getVersionString, (), (const, override));
+    MOCK_METHOD(Version, getFeatureLevel, (), (const, override));
+    MOCK_METHOD(DeviceType, getType, (), (const, override));
+    MOCK_METHOD(const std::vector<Extension>&, getSupportedExtensions, (), (const, override));
+    MOCK_METHOD(const Capabilities&, getCapabilities, (), (const, override));
+    MOCK_METHOD((std::pair<uint32_t, uint32_t>), getNumberOfCacheFilesNeeded, (),
+                (const, override));
+    MOCK_METHOD(GeneralResult<void>, wait, (), (const, override));
+    MOCK_METHOD(GeneralResult<std::vector<bool>>, getSupportedOperations, (const Model& model),
+                (const, override));
+    MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModel,
+                (const Model& model, ExecutionPreference preference, Priority priority,
+                 OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
+                 const std::vector<SharedHandle>& dataCache, const CacheToken& token),
+                (const, override));
+    MOCK_METHOD(GeneralResult<SharedPreparedModel>, prepareModelFromCache,
+                (OptionalTimePoint deadline, const std::vector<SharedHandle>& modelCache,
+                 const std::vector<SharedHandle>& dataCache, const CacheToken& token),
+                (const, override));
+    MOCK_METHOD(GeneralResult<SharedBuffer>, allocate,
+                (const BufferDesc& desc, const std::vector<SharedPreparedModel>& preparedModels,
+                 const std::vector<BufferRole>& inputRoles,
+                 const std::vector<BufferRole>& outputRoles),
+                (const, override));
+};
+
+}  // namespace android::nn
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_DEVICE_H
diff --git a/neuralnetworks/utils/common/test/MockExecution.h b/neuralnetworks/utils/common/test/MockExecution.h
new file mode 100644
index 0000000..91e3428
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockExecution.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IExecution.h>
+
+namespace android::nn {
+
+class MockExecution final : public IExecution {
+  public:
+    MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), compute,
+                (const OptionalTimePoint& deadline), (const, override));
+    MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), computeFenced,
+                (const std::vector<SyncFence>& waitFor, const OptionalTimePoint& deadline,
+                 const OptionalDuration& timeoutDurationAfterFence),
+                (const, override));
+};
+
+}  // namespace android::nn
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_EXECUTION
diff --git a/neuralnetworks/utils/common/test/MockPreparedModel.h b/neuralnetworks/utils/common/test/MockPreparedModel.h
new file mode 100644
index 0000000..c8ce006
--- /dev/null
+++ b/neuralnetworks/utils/common/test/MockPreparedModel.h
@@ -0,0 +1,48 @@
+/*
+ * 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_TEST_MOCK_PREPARED_MODEL_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <nnapi/IPreparedModel.h>
+
+namespace android::nn {
+
+class MockPreparedModel final : public IPreparedModel {
+  public:
+    MOCK_METHOD((ExecutionResult<std::pair<std::vector<OutputShape>, Timing>>), execute,
+                (const Request& request, MeasureTiming measure, const OptionalTimePoint& deadline,
+                 const OptionalDuration& loopTimeoutDuration),
+                (const, override));
+    MOCK_METHOD((GeneralResult<std::pair<SyncFence, ExecuteFencedInfoCallback>>), executeFenced,
+                (const Request& request, const std::vector<SyncFence>& waitFor,
+                 MeasureTiming measure, const OptionalTimePoint& deadline,
+                 const OptionalDuration& loopTimeoutDuration,
+                 const OptionalDuration& timeoutDurationAfterFence),
+                (const, override));
+    MOCK_METHOD((GeneralResult<SharedExecution>), createReusableExecution,
+                (const nn::Request& request, nn::MeasureTiming measure,
+                 const nn::OptionalDuration& loopTimeoutDuration),
+                (const, override));
+    MOCK_METHOD(GeneralResult<SharedBurst>, configureExecutionBurst, (), (const, override));
+    MOCK_METHOD(std::any, getUnderlyingResource, (), (const, override));
+};
+
+}  // namespace android::nn
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_COMMON_TEST_MOCK_PREPARED_MODEL_H
diff --git a/neuralnetworks/utils/common/test/ResilientBufferTest.cpp b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
new file mode 100644
index 0000000..7afd020
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientBufferTest.cpp
@@ -0,0 +1,276 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <nnapi/SharedMemory.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientBuffer.h>
+#include <memory>
+#include <tuple>
+#include <utility>
+#include "MockBuffer.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+constexpr auto kToken = nn::Request::MemoryDomainToken{1};
+
+using SharedMockBuffer = std::shared_ptr<const nn::MockBuffer>;
+using MockBufferFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedBuffer>()>;
+
+SharedMockBuffer createConfiguredMockBuffer() {
+    return std::make_shared<const nn::MockBuffer>();
+}
+
+std::tuple<std::shared_ptr<const nn::MockBuffer>, std::unique_ptr<MockBufferFactory>,
+           std::shared_ptr<const ResilientBuffer>>
+setup() {
+    auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+
+    auto mockBufferFactory = std::make_unique<MockBufferFactory>();
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(mockBuffer));
+
+    auto buffer = ResilientBuffer::create(mockBufferFactory->AsStdFunction()).value();
+    return std::make_tuple(std::move(mockBuffer), std::move(mockBufferFactory), std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+    return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoError = nn::GeneralResult<void>{};
+
+}  // namespace
+
+TEST(ResilientBufferTest, invalidBufferFactory) {
+    // setup call
+    const auto invalidBufferFactory = ResilientBuffer::Factory{};
+
+    // run test
+    const auto result = ResilientBuffer::create(invalidBufferFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientBufferTest, bufferFactoryFailure) {
+    // setup call
+    const auto invalidBufferFactory = kReturnGeneralFailure;
+
+    // run test
+    const auto result = ResilientBuffer::create(invalidBufferFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, getBuffer) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+
+    // run test
+    const auto result = buffer->getBuffer();
+
+    // verify result
+    EXPECT_TRUE(result == mockBuffer);
+}
+
+TEST(ResilientBufferTest, getToken) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, getToken()).Times(1).WillOnce(Return(kToken));
+
+    // run test
+    const auto token = buffer->getToken();
+
+    // verify result
+    EXPECT_EQ(token, kToken);
+}
+
+TEST(ResilientBufferTest, copyTo) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyToError) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, copyToDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientBufferTest, copyToDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyTo(_)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockBuffer = createConfiguredMockBuffer();
+    EXPECT_CALL(*recoveredMockBuffer, copyTo(_)).Times(1).WillOnce(Return(kNoError));
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyTo(memory);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyFrom) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, copyFromError) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientBufferTest, copyFromDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientBufferTest, copyFromDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    EXPECT_CALL(*mockBuffer, copyFrom(_, _)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockBuffer = createConfiguredMockBuffer();
+    EXPECT_CALL(*recoveredMockBuffer, copyFrom(_, _)).Times(1).WillOnce(Return(kNoError));
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+    // run test
+    const nn::SharedMemory memory = std::make_shared<const nn::Memory>();
+    const auto result = buffer->copyFrom(memory, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientBufferTest, recover) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    const auto recoveredMockBuffer = createConfiguredMockBuffer();
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+
+    // run test
+    const auto result = buffer->recover(mockBuffer.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockBuffer);
+}
+
+TEST(ResilientBufferTest, recoverFailure) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    const auto recoveredMockBuffer = createConfiguredMockBuffer();
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = buffer->recover(mockBuffer.get());
+
+    // verify result
+    EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientBufferTest, someoneElseRecovered) {
+    // setup call
+    const auto [mockBuffer, mockBufferFactory, buffer] = setup();
+    const auto recoveredMockBuffer = createConfiguredMockBuffer();
+    EXPECT_CALL(*mockBufferFactory, Call()).Times(1).WillOnce(Return(recoveredMockBuffer));
+    buffer->recover(mockBuffer.get());
+
+    // run test
+    const auto result = buffer->recover(mockBuffer.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockBuffer);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
new file mode 100644
index 0000000..3abd724
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientDeviceTest.cpp
@@ -0,0 +1,725 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientDevice.h>
+#include <tuple>
+#include <utility>
+#include "MockBuffer.h"
+#include "MockDevice.h"
+#include "MockPreparedModel.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockDevice = std::shared_ptr<const nn::MockDevice>;
+using MockDeviceFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedDevice>(bool)>;
+
+const std::string kName = "Google-MockV1";
+const std::string kVersionString = "version1";
+const auto kExtensions = std::vector<nn::Extension>{};
+constexpr auto kNoInfo = std::numeric_limits<float>::max();
+constexpr auto kNoPerformanceInfo =
+        nn::Capabilities::PerformanceInfo{.execTime = kNoInfo, .powerUsage = kNoInfo};
+const auto kCapabilities = nn::Capabilities{
+        .relaxedFloat32toFloat16PerformanceScalar = kNoPerformanceInfo,
+        .relaxedFloat32toFloat16PerformanceTensor = kNoPerformanceInfo,
+        .operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value(),
+        .ifPerformance = kNoPerformanceInfo,
+        .whilePerformance = kNoPerformanceInfo};
+constexpr auto kNumberOfCacheFilesNeeded = std::pair<uint32_t, uint32_t>(5, 3);
+
+SharedMockDevice createConfiguredMockDevice() {
+    auto mockDevice = std::make_shared<const nn::MockDevice>();
+
+    // Setup default actions for each relevant call.
+    constexpr auto getName_ret = []() -> const std::string& { return kName; };
+    constexpr auto getVersionString_ret = []() -> const std::string& { return kVersionString; };
+    constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+    constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
+    constexpr auto getSupportedExtensions_ret = []() -> const std::vector<nn::Extension>& {
+        return kExtensions;
+    };
+    constexpr auto getCapabilities_ret = []() -> const nn::Capabilities& { return kCapabilities; };
+
+    // Setup default actions for each relevant call.
+    ON_CALL(*mockDevice, getName()).WillByDefault(getName_ret);
+    ON_CALL(*mockDevice, getVersionString()).WillByDefault(getVersionString_ret);
+    ON_CALL(*mockDevice, getFeatureLevel()).WillByDefault(Return(kFeatureLevel));
+    ON_CALL(*mockDevice, getType()).WillByDefault(Return(kDeviceType));
+    ON_CALL(*mockDevice, getSupportedExtensions()).WillByDefault(getSupportedExtensions_ret);
+    ON_CALL(*mockDevice, getCapabilities()).WillByDefault(getCapabilities_ret);
+    ON_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
+            .WillByDefault(Return(kNumberOfCacheFilesNeeded));
+
+    // These EXPECT_CALL(...).Times(testing::AnyNumber()) calls are to suppress warnings on the
+    // uninteresting methods calls.
+    EXPECT_CALL(*mockDevice, getName()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getVersionString()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getType()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getSupportedExtensions()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getCapabilities()).Times(testing::AnyNumber());
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded()).Times(testing::AnyNumber());
+
+    return mockDevice;
+}
+
+std::tuple<SharedMockDevice, std::unique_ptr<MockDeviceFactory>,
+           std::shared_ptr<const ResilientDevice>>
+setup() {
+    auto mockDevice = createConfiguredMockDevice();
+
+    auto mockDeviceFactory = std::make_unique<MockDeviceFactory>();
+    EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(mockDevice));
+
+    auto device = ResilientDevice::create(mockDeviceFactory->AsStdFunction()).value();
+    return std::make_tuple(std::move(mockDevice), std::move(mockDeviceFactory), std::move(device));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+    return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+}  // namespace
+
+TEST(ResilientDeviceTest, invalidDeviceFactory) {
+    // setup call
+    const auto invalidDeviceFactory = ResilientDevice::Factory{};
+
+    // run test
+    const auto result = ResilientDevice::create(invalidDeviceFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientDeviceTest, preparedModelFactoryFailure) {
+    // setup call
+    const auto invalidDeviceFactory = kReturnGeneralFailure;
+
+    // run test
+    const auto result = ResilientDevice::create(invalidDeviceFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, cachedData) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+
+    // run test and verify results
+    EXPECT_EQ(device->getName(), kName);
+    EXPECT_EQ(device->getVersionString(), kVersionString);
+    EXPECT_EQ(device->getSupportedExtensions(), kExtensions);
+    EXPECT_EQ(device->getCapabilities(), kCapabilities);
+}
+
+TEST(ResilientDeviceTest, getFeatureLevel) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    constexpr auto kFeatureLevel = nn::Version::ANDROID_OC_MR1;
+    EXPECT_CALL(*mockDevice, getFeatureLevel()).Times(1).WillOnce(Return(kFeatureLevel));
+
+    // run test
+    const auto featureLevel = device->getFeatureLevel();
+
+    // verify results
+    EXPECT_EQ(featureLevel, kFeatureLevel);
+}
+
+TEST(ResilientDeviceTest, getType) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    constexpr auto kDeviceType = nn::DeviceType::ACCELERATOR;
+    EXPECT_CALL(*mockDevice, getType()).Times(1).WillOnce(Return(kDeviceType));
+
+    // run test
+    const auto type = device->getType();
+
+    // verify results
+    EXPECT_EQ(type, kDeviceType);
+}
+
+TEST(ResilientDeviceTest, getNumberOfCacheFilesNeeded) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, getNumberOfCacheFilesNeeded())
+            .Times(1)
+            .WillOnce(Return(kNumberOfCacheFilesNeeded));
+
+    // run test
+    const auto numberOfCacheFilesNeeded = device->getNumberOfCacheFilesNeeded();
+
+    // verify results
+    EXPECT_EQ(numberOfCacheFilesNeeded, kNumberOfCacheFilesNeeded);
+}
+
+TEST(ResilientDeviceTest, getDevice) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+
+    // run test
+    const auto result = device->getDevice();
+
+    // verify result
+    EXPECT_TRUE(result == mockDevice);
+}
+
+TEST(ResilientDeviceTest, wait) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, waitError) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, waitDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, waitDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, wait()).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, wait()).Times(1).WillOnce(Return(nn::GeneralResult<void>{}));
+    EXPECT_CALL(*mockDeviceFactory, Call(true)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->wait();
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, getSupportedOperations) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_))
+            .Times(1)
+            .WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
+
+    // run test
+    const auto result = device->getSupportedOperations({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsError) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->getSupportedOperations({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->getSupportedOperations({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, getSupportedOperationsDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, getSupportedOperations(_)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getSupportedOperations(_))
+            .Times(1)
+            .WillOnce(Return(nn::GeneralResult<std::vector<bool>>{}));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->getSupportedOperations({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModel) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Return(mockPreparedModel));
+
+    // run test
+    const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelError) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, prepareModelDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, prepareModelDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModel(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+    EXPECT_CALL(*recoveredMockDevice, prepareModel(_, _, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Return(mockPreparedModel));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCache) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Return(mockPreparedModel));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheError) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, prepareModelFromCacheDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+    EXPECT_CALL(*recoveredMockDevice, prepareModelFromCache(_, _, _, _))
+            .Times(1)
+            .WillOnce(Return(mockPreparedModel));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, allocate) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, allocateError) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientDeviceTest, allocateDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientDeviceTest, allocateDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    EXPECT_CALL(*mockDevice, allocate(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const auto mockBuffer = std::make_shared<const nn::MockBuffer>();
+    EXPECT_CALL(*recoveredMockDevice, allocate(_, _, _, _)).Times(1).WillOnce(Return(mockBuffer));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientDeviceTest, recover) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverFailure) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*mockDeviceFactory, Call(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientDeviceTest, someoneElseRecovered) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+    device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetName) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const std::string kDifferentName = "Google-DifferentName";
+    const auto ret = [&kDifferentName]() -> const std::string& { return kDifferentName; };
+    EXPECT_CALL(*recoveredMockDevice, getName()).Times(1).WillOnce(ret);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetVersionString) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const std::string kDifferentVersionString = "differentversion";
+    const auto ret = [&kDifferentVersionString]() -> const std::string& {
+        return kDifferentVersionString;
+    };
+    EXPECT_CALL(*recoveredMockDevice, getVersionString()).Times(1).WillOnce(ret);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetFeatureLevel) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getFeatureLevel())
+            .Times(1)
+            .WillOnce(Return(nn::Version::ANDROID_P));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetType) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetSupportedExtensions) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const auto kDifferentExtensions =
+            std::vector<nn::Extension>{nn::Extension{.name = "", .operandTypes = {}}};
+    const auto ret = [&kDifferentExtensions]() -> const std::vector<nn::Extension>& {
+        return kDifferentExtensions;
+    };
+    EXPECT_CALL(*recoveredMockDevice, getSupportedExtensions()).Times(1).WillOnce(ret);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchGetCapabilities) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    const auto kDifferentCapabilities = nn::Capabilities{
+            .relaxedFloat32toFloat16PerformanceTensor = {.execTime = 0.5f, .powerUsage = 0.5f},
+            .operandPerformance = nn::Capabilities::OperandPerformanceTable::create({}).value()};
+    const auto ret = [&kDifferentCapabilities]() -> const nn::Capabilities& {
+        return kDifferentCapabilities;
+    };
+    EXPECT_CALL(*recoveredMockDevice, getCapabilities()).Times(1).WillOnce(ret);
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+
+    // run test
+    const auto result = device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+    EXPECT_TRUE(result.value() != mockDevice);
+    EXPECT_TRUE(result.value() != recoveredMockDevice);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModel) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+    device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // run test
+    auto result = device->prepareModel({}, {}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidPrepareModelFromCache) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+    device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // run test
+    auto result = device->prepareModelFromCache({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+}
+
+TEST(ResilientDeviceTest, recoverCacheMismatchInvalidAllocate) {
+    // setup call
+    const auto [mockDevice, mockDeviceFactory, device] = setup();
+    const auto recoveredMockDevice = createConfiguredMockDevice();
+    EXPECT_CALL(*recoveredMockDevice, getType()).Times(1).WillOnce(Return(nn::DeviceType::GPU));
+    EXPECT_CALL(*mockDeviceFactory, Call(false)).Times(1).WillOnce(Return(recoveredMockDevice));
+    device->recover(mockDevice.get(), /*blocking=*/false);
+
+    // run test
+    auto result = device->allocate({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() != nullptr);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientExecution.cpp b/neuralnetworks/utils/common/test/ResilientExecution.cpp
new file mode 100644
index 0000000..c0737fb
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientExecution.cpp
@@ -0,0 +1,260 @@
+/*
+ * 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.
+ */
+
+#include <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientExecution.h>
+#include <utility>
+#include "MockExecution.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockExecution = std::shared_ptr<const nn::MockExecution>;
+using MockExecutionFactory = ::testing::MockFunction<nn::GeneralResult<nn::SharedExecution>()>;
+
+SharedMockExecution createMockExecution() {
+    return std::make_shared<const nn::MockExecution>();
+}
+
+std::tuple<SharedMockExecution, std::unique_ptr<MockExecutionFactory>,
+           std::shared_ptr<const ResilientExecution>>
+setup() {
+    auto mockExecution = std::make_shared<const nn::MockExecution>();
+
+    auto mockExecutionFactory = std::make_unique<MockExecutionFactory>();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(mockExecution));
+
+    auto buffer = ResilientExecution::create(mockExecutionFactory->AsStdFunction()).value();
+    return std::make_tuple(std::move(mockExecution), std::move(mockExecutionFactory),
+                           std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+    return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoExecutionError =
+        nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
+const auto kNoFencedExecutionError =
+        nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
+                std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
+
+}  // namespace
+
+TEST(ResilientExecutionTest, invalidExecutionFactory) {
+    // setup call
+    const auto invalidExecutionFactory = ResilientExecution::Factory{};
+
+    // run test
+    const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientExecutionTest, executionFactoryFailure) {
+    // setup call
+    const auto invalidExecutionFactory = kReturnGeneralFailure;
+
+    // run test
+    const auto result = ResilientExecution::create(invalidExecutionFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, getExecution) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+
+    // run test
+    const auto result = execution->getExecution();
+
+    // verify result
+    EXPECT_TRUE(result == mockExecution);
+}
+
+TEST(ResilientExecutionTest, compute) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeError) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, compute(_)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*recoveredMockExecution, compute(_)).Times(1).WillOnce(Return(kNoExecutionError));
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->compute({});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFenced) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, computeFencedError) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientExecutionTest, computeFencedDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    EXPECT_CALL(*mockExecution, computeFenced(_, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*recoveredMockExecution, computeFenced(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->computeFenced({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientExecutionTest, recover) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+TEST(ResilientExecutionTest, recoverFailure) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientExecutionTest, someoneElseRecovered) {
+    // setup call
+    const auto [mockExecution, mockExecutionFactory, execution] = setup();
+    const auto recoveredMockExecution = createMockExecution();
+    EXPECT_CALL(*mockExecutionFactory, Call()).Times(1).WillOnce(Return(recoveredMockExecution));
+    execution->recover(mockExecution.get());
+
+    // run test
+    const auto result = execution->recover(mockExecution.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockExecution);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
new file mode 100644
index 0000000..d396ca8
--- /dev/null
+++ b/neuralnetworks/utils/common/test/ResilientPreparedModelTest.cpp
@@ -0,0 +1,328 @@
+/*
+ * 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 <gmock/gmock.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/ResilientPreparedModel.h>
+#include <utility>
+#include "MockPreparedModel.h"
+
+namespace android::hardware::neuralnetworks::utils {
+namespace {
+
+using ::testing::_;
+using ::testing::InvokeWithoutArgs;
+using ::testing::Return;
+
+using SharedMockPreparedModel = std::shared_ptr<const nn::MockPreparedModel>;
+using MockPreparedModelFactory =
+        ::testing::MockFunction<nn::GeneralResult<nn::SharedPreparedModel>()>;
+
+SharedMockPreparedModel createConfiguredMockPreparedModel() {
+    return std::make_shared<const nn::MockPreparedModel>();
+}
+
+std::tuple<std::shared_ptr<const nn::MockPreparedModel>, std::unique_ptr<MockPreparedModelFactory>,
+           std::shared_ptr<const ResilientPreparedModel>>
+setup() {
+    auto mockPreparedModel = std::make_shared<const nn::MockPreparedModel>();
+
+    auto mockPreparedModelFactory = std::make_unique<MockPreparedModelFactory>();
+    EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(Return(mockPreparedModel));
+
+    auto buffer = ResilientPreparedModel::create(mockPreparedModelFactory->AsStdFunction()).value();
+    return std::make_tuple(std::move(mockPreparedModel), std::move(mockPreparedModelFactory),
+                           std::move(buffer));
+}
+
+constexpr auto makeError = [](nn::ErrorStatus status) {
+    return [status](const auto&... /*args*/) { return nn::error(status); };
+};
+const auto kReturnGeneralFailure = makeError(nn::ErrorStatus::GENERAL_FAILURE);
+const auto kReturnDeadObject = makeError(nn::ErrorStatus::DEAD_OBJECT);
+
+const auto kNoCreateReusableExecutionError = nn::GeneralResult<nn::SharedExecution>{};
+const auto kNoExecutionError =
+        nn::ExecutionResult<std::pair<std::vector<nn::OutputShape>, nn::Timing>>{};
+const auto kNoFencedExecutionError =
+        nn::GeneralResult<std::pair<nn::SyncFence, nn::ExecuteFencedInfoCallback>>(
+                std::make_pair(nn::SyncFence::createAsSignaled(), nullptr));
+
+struct FakeResource {};
+
+}  // namespace
+
+TEST(ResilientPreparedModelTest, invalidPreparedModelFactory) {
+    // setup call
+    const auto invalidPreparedModelFactory = ResilientPreparedModel::Factory{};
+
+    // run test
+    const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::INVALID_ARGUMENT);
+}
+
+TEST(ResilientPreparedModelTest, preparedModelFactoryFailure) {
+    // setup call
+    const auto invalidPreparedModelFactory = kReturnGeneralFailure;
+
+    // run test
+    const auto result = ResilientPreparedModel::create(invalidPreparedModelFactory);
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, getPreparedModel) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+
+    // run test
+    const auto result = preparedModel->getPreparedModel();
+
+    // verify result
+    EXPECT_TRUE(result == mockPreparedModel);
+}
+
+TEST(ResilientPreparedModelTest, execute) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoExecutionError));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeError) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, executeDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    constexpr auto ret = [] { return nn::error(nn::ErrorStatus::GENERAL_FAILURE); };
+    EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(ret);
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientPreparedModelTest, executeDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, execute(_, _, _, _)).Times(1).WillOnce(kReturnDeadObject);
+    const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+    EXPECT_CALL(*recoveredMockPreparedModel, execute(_, _, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoExecutionError));
+    EXPECT_CALL(*mockPreparedModelFactory, Call())
+            .Times(1)
+            .WillOnce(Return(recoveredMockPreparedModel));
+
+    // run test
+    const auto result = preparedModel->execute({}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeFenced) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, executeFencedError) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, executeFencedDeadObjectFailedRecovery) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::DEAD_OBJECT);
+}
+
+TEST(ResilientPreparedModelTest, executeFencedDeadObjectSuccessfulRecovery) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, executeFenced(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(kReturnDeadObject);
+    const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+    EXPECT_CALL(*recoveredMockPreparedModel, executeFenced(_, _, _, _, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoFencedExecutionError));
+    EXPECT_CALL(*mockPreparedModelFactory, Call())
+            .Times(1)
+            .WillOnce(Return(recoveredMockPreparedModel));
+
+    // run test
+    const auto result = preparedModel->executeFenced({}, {}, {}, {}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, createReusableExecution) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+            .Times(1)
+            .WillOnce(Return(kNoCreateReusableExecutionError));
+
+    // run test
+    const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+}
+
+TEST(ResilientPreparedModelTest, createReusableExecutionError) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, createReusableExecution(_, _, _))
+            .Times(1)
+            .WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->createReusableExecution({}, {}, {});
+
+    // verify result
+    ASSERT_FALSE(result.has_value());
+    EXPECT_EQ(result.error().code, nn::ErrorStatus::GENERAL_FAILURE);
+}
+
+TEST(ResilientPreparedModelTest, getUnderlyingResource) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    EXPECT_CALL(*mockPreparedModel, getUnderlyingResource())
+            .Times(1)
+            .WillOnce(Return(FakeResource{}));
+
+    // run test
+    const auto resource = preparedModel->getUnderlyingResource();
+
+    // verify resource
+    const FakeResource* maybeFakeResource = std::any_cast<FakeResource>(&resource);
+    EXPECT_NE(maybeFakeResource, nullptr);
+}
+
+TEST(ResilientPreparedModelTest, recover) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModelFactory, Call())
+            .Times(1)
+            .WillOnce(Return(recoveredMockPreparedModel));
+
+    // run test
+    const auto result = preparedModel->recover(mockPreparedModel.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
+}
+
+TEST(ResilientPreparedModelTest, recoverFailure) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModelFactory, Call()).Times(1).WillOnce(kReturnGeneralFailure);
+
+    // run test
+    const auto result = preparedModel->recover(mockPreparedModel.get());
+
+    // verify result
+    EXPECT_FALSE(result.has_value());
+}
+
+TEST(ResilientPreparedModelTest, someoneElseRecovered) {
+    // setup call
+    const auto [mockPreparedModel, mockPreparedModelFactory, preparedModel] = setup();
+    const auto recoveredMockPreparedModel = createConfiguredMockPreparedModel();
+    EXPECT_CALL(*mockPreparedModelFactory, Call())
+            .Times(1)
+            .WillOnce(Return(recoveredMockPreparedModel));
+    preparedModel->recover(mockPreparedModel.get());
+
+    // run test
+    const auto result = preparedModel->recover(mockPreparedModel.get());
+
+    // verify result
+    ASSERT_TRUE(result.has_value())
+            << "Failed with " << result.error().code << ": " << result.error().message;
+    EXPECT_TRUE(result.value() == recoveredMockPreparedModel);
+}
+
+}  // namespace android::hardware::neuralnetworks::utils
diff --git a/neuralnetworks/utils/service/Android.bp b/neuralnetworks/utils/service/Android.bp
new file mode 100644
index 0000000..5f36dff
--- /dev/null
+++ b/neuralnetworks/utils/service/Android.bp
@@ -0,0 +1,49 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library_static {
+    name: "neuralnetworks_utils_hal_service",
+    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",
+        "neuralnetworks_utils_hal_aidl",
+        "neuralnetworks_utils_hal_common",
+    ],
+    shared_libs: [
+        "android.hardware.neuralnetworks-V1-ndk_platform",
+        "android.hardware.neuralnetworks@1.0",
+        "android.hardware.neuralnetworks@1.1",
+        "android.hardware.neuralnetworks@1.2",
+        "android.hardware.neuralnetworks@1.3",
+        "libbinder_ndk",
+    ],
+}
diff --git a/neuralnetworks/utils/service/include/nnapi/hal/Service.h b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
new file mode 100644
index 0000000..2fd5237
--- /dev/null
+++ b/neuralnetworks/utils/service/include/nnapi/hal/Service.h
@@ -0,0 +1,36 @@
+/*
+ * 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_SERVICE_H
+#define ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
+
+#include <nnapi/IDevice.h>
+#include <nnapi/Types.h>
+#include <memory>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::service {
+
+struct SharedDeviceAndUpdatability {
+    nn::SharedDevice device;
+    bool isDeviceUpdatable = false;
+};
+
+std::vector<SharedDeviceAndUpdatability> getDevices(bool includeUpdatableDrivers);
+
+}  // namespace android::hardware::neuralnetworks::service
+
+#endif  // ANDROID_HARDWARE_INTERFACES_NEURALNETWORKS_UTILS_SERVICE_H
diff --git a/neuralnetworks/utils/service/src/Service.cpp b/neuralnetworks/utils/service/src/Service.cpp
new file mode 100644
index 0000000..2286288
--- /dev/null
+++ b/neuralnetworks/utils/service/src/Service.cpp
@@ -0,0 +1,137 @@
+/*
+ * 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 "Service.h"
+
+#include <AndroidVersionUtil.h>
+#include <aidl/android/hardware/neuralnetworks/IDevice.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/hardware/neuralnetworks/1.0/IDevice.h>
+#include <android/hardware/neuralnetworks/1.1/IDevice.h>
+#include <android/hardware/neuralnetworks/1.2/IDevice.h>
+#include <android/hardware/neuralnetworks/1.3/IDevice.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
+#include <hidl/ServiceManagement.h>
+#include <nnapi/IDevice.h>
+#include <nnapi/Result.h>
+#include <nnapi/TypeUtils.h>
+#include <nnapi/Types.h>
+#include <nnapi/hal/1.0/Service.h>
+#include <nnapi/hal/1.1/Service.h>
+#include <nnapi/hal/1.2/Service.h>
+#include <nnapi/hal/1.3/Service.h>
+#include <nnapi/hal/aidl/Service.h>
+#include <nnapi/hal/aidl/Utils.h>
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <type_traits>
+#include <unordered_set>
+#include <vector>
+
+namespace android::hardware::neuralnetworks::service {
+namespace {
+
+namespace aidl_hal = ::aidl::android::hardware::neuralnetworks;
+using getDeviceFn = std::add_pointer_t<nn::GeneralResult<nn::SharedDevice>(const std::string&)>;
+
+void getHidlDevicesForVersion(const std::string& descriptor, getDeviceFn getDevice,
+                              std::vector<SharedDeviceAndUpdatability>* devices,
+                              std::unordered_set<std::string>* registeredDevices) {
+    CHECK(devices != nullptr);
+    CHECK(registeredDevices != nullptr);
+
+    const auto names = getAllHalInstanceNames(descriptor);
+    for (const auto& name : names) {
+        if (const auto [it, unregistered] = registeredDevices->insert(name); unregistered) {
+            auto maybeDevice = getDevice(name);
+            if (maybeDevice.has_value()) {
+                auto device = std::move(maybeDevice).value();
+                CHECK(device != nullptr);
+                devices->push_back({.device = std::move(device)});
+            } else {
+                LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
+                           << ": " << maybeDevice.error().message;
+            }
+        }
+    }
+}
+
+void getAidlDevices(std::vector<SharedDeviceAndUpdatability>* devices,
+                    std::unordered_set<std::string>* registeredDevices,
+                    bool includeUpdatableDrivers) {
+    CHECK(devices != nullptr);
+    CHECK(registeredDevices != nullptr);
+
+    std::vector<std::string> names;
+    constexpr auto callback = [](const char* serviceName, void* names) {
+        static_cast<std::vector<std::string>*>(names)->emplace_back(serviceName);
+    };
+
+    // Devices with SDK level lower than 31 (Android S) don't have any AIDL drivers available, so
+    // there is no need for a workaround supported on lower levels.
+    if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
+        AServiceManager_forEachDeclaredInstance(aidl_hal::IDevice::descriptor,
+                                                static_cast<void*>(&names), callback);
+    }
+
+    for (const auto& name : names) {
+        bool isDeviceUpdatable = false;
+        if (__builtin_available(android __NNAPI_AIDL_MIN_ANDROID_API__, *)) {
+            const auto instance = std::string(aidl_hal::IDevice::descriptor) + '/' + name;
+            isDeviceUpdatable = AServiceManager_isUpdatableViaApex(instance.c_str());
+        }
+        if (isDeviceUpdatable && !includeUpdatableDrivers) {
+            continue;
+        }
+        if (const auto [it, unregistered] = registeredDevices->insert(name); unregistered) {
+            auto maybeDevice = aidl_hal::utils::getDevice(name);
+            if (maybeDevice.has_value()) {
+                auto device = std::move(maybeDevice).value();
+                CHECK(device != nullptr);
+                devices->push_back(
+                        {.device = std::move(device), .isDeviceUpdatable = isDeviceUpdatable});
+            } else {
+                LOG(ERROR) << "getDevice(" << name << ") failed with " << maybeDevice.error().code
+                           << ": " << maybeDevice.error().message;
+            }
+        }
+    }
+}
+
+}  // namespace
+
+std::vector<SharedDeviceAndUpdatability> getDevices(bool includeUpdatableDrivers) {
+    std::vector<SharedDeviceAndUpdatability> devices;
+    std::unordered_set<std::string> registeredDevices;
+
+    getAidlDevices(&devices, &registeredDevices, includeUpdatableDrivers);
+
+    getHidlDevicesForVersion(V1_3::IDevice::descriptor, &V1_3::utils::getDevice, &devices,
+                             &registeredDevices);
+    getHidlDevicesForVersion(V1_2::IDevice::descriptor, &V1_2::utils::getDevice, &devices,
+                             &registeredDevices);
+    getHidlDevicesForVersion(V1_1::IDevice::descriptor, &V1_1::utils::getDevice, &devices,
+                             &registeredDevices);
+    getHidlDevicesForVersion(V1_0::IDevice::descriptor, &V1_0::utils::getDevice, &devices,
+                             &registeredDevices);
+
+    return devices;
+}
+
+}  // namespace android::hardware::neuralnetworks::service
diff --git a/nfc/1.0/Android.bp b/nfc/1.0/Android.bp
index bd64907..55c8639 100644
--- a/nfc/1.0/Android.bp
+++ b/nfc/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.nfc@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "INfc.hal",
diff --git a/nfc/1.0/default/Android.bp b/nfc/1.0/default/Android.bp
index 9827edd..a0cefee 100644
--- a/nfc/1.0/default/Android.bp
+++ b/nfc/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.nfc@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/nfc/1.0/vts/functional/Android.bp b/nfc/1.0/vts/functional/Android.bp
index 40ba22e..0d3f0c9 100644
--- a/nfc/1.0/vts/functional/Android.bp
+++ b/nfc/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalNfcV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/nfc/1.0/vts/functional/AndroidTest.xml b/nfc/1.0/vts/functional/AndroidTest.xml
index 364672b..76aca48 100644
--- a/nfc/1.0/vts/functional/AndroidTest.xml
+++ b/nfc/1.0/vts/functional/AndroidTest.xml
@@ -28,6 +28,6 @@
     <test class="com.android.tradefed.testtype.GTest" >
         <option name="native-test-device-path" value="/data/local/tmp" />
         <option name="module-name" value="VtsHalNfcV1_0TargetTest" />
-        <option name="native-test-timeout" value="180000"/>
+        <option name="native-test-timeout" value="600000"/>
     </test>
 </configuration>
diff --git a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
index 1feae9d..4d997e6 100644
--- a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
+++ b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
@@ -578,6 +578,7 @@
   EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, NfcHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(INfc::descriptor)),
diff --git a/nfc/1.1/Android.bp b/nfc/1.1/Android.bp
index 1f8789f..a8463cf 100644
--- a/nfc/1.1/Android.bp
+++ b/nfc/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.nfc@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "INfc.hal",
diff --git a/nfc/1.1/vts/functional/Android.bp b/nfc/1.1/vts/functional/Android.bp
index 1c18418..4439531 100644
--- a/nfc/1.1/vts/functional/Android.bp
+++ b/nfc/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalNfcV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/nfc/1.1/vts/functional/VtsHalNfcV1_1TargetTest.cpp b/nfc/1.1/vts/functional/VtsHalNfcV1_1TargetTest.cpp
index 13537e4..1c4c78a 100644
--- a/nfc/1.1/vts/functional/VtsHalNfcV1_1TargetTest.cpp
+++ b/nfc/1.1/vts/functional/VtsHalNfcV1_1TargetTest.cpp
@@ -211,6 +211,7 @@
     });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, NfcHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(INfc::descriptor)),
diff --git a/nfc/1.2/Android.bp b/nfc/1.2/Android.bp
index aa68d2f..4831ab9 100644
--- a/nfc/1.2/Android.bp
+++ b/nfc/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.nfc@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "INfc.hal",
diff --git a/nfc/1.2/vts/functional/Android.bp b/nfc/1.2/vts/functional/Android.bp
index 83e7a8e..ff7bd3a 100644
--- a/nfc/1.2/vts/functional/Android.bp
+++ b/nfc/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalNfcV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/nfc/1.2/vts/functional/VtsHalNfcV1_2TargetTest.cpp b/nfc/1.2/vts/functional/VtsHalNfcV1_2TargetTest.cpp
index 3ec088d..e4cb12b 100644
--- a/nfc/1.2/vts/functional/VtsHalNfcV1_2TargetTest.cpp
+++ b/nfc/1.2/vts/functional/VtsHalNfcV1_2TargetTest.cpp
@@ -155,6 +155,7 @@
     });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(NfcHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, NfcHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(INfc::descriptor)),
diff --git a/oemlock/1.0/Android.bp b/oemlock/1.0/Android.bp
index e784be0..da536c4 100644
--- a/oemlock/1.0/Android.bp
+++ b/oemlock/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.oemlock@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IOemLock.hal",
diff --git a/oemlock/1.0/vts/functional/Android.bp b/oemlock/1.0/vts/functional/Android.bp
index 4dd92b5..f1b8d2f 100644
--- a/oemlock/1.0/vts/functional/Android.bp
+++ b/oemlock/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalOemLockV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp b/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp
index bafe87d..76c6440 100644
--- a/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp
+++ b/oemlock/1.0/vts/functional/VtsHalOemLockV1_0TargetTest.cpp
@@ -188,6 +188,7 @@
     ASSERT_EQ(allowed, originallyAllowed);
 };
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OemLockHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, OemLockHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOemLock::descriptor)),
diff --git a/oemlock/aidl/Android.bp b/oemlock/aidl/Android.bp
new file mode 100644
index 0000000..439d43d
--- /dev/null
+++ b/oemlock/aidl/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.oemlock",
+    vendor_available: true,
+    srcs: ["android/hardware/oemlock/*.aidl"],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl
new file mode 100644
index 0000000..e3c974d
--- /dev/null
+++ b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/IOemLock.aidl
@@ -0,0 +1,27 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.oemlock;
+@VintfStability
+interface IOemLock {
+  String getName();
+  boolean isOemUnlockAllowedByCarrier();
+  boolean isOemUnlockAllowedByDevice();
+  android.hardware.oemlock.OemLockSecureStatus setOemUnlockAllowedByCarrier(in boolean allowed, in byte[] signature);
+  void setOemUnlockAllowedByDevice(in boolean allowed);
+}
diff --git a/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl
new file mode 100644
index 0000000..9d1327d
--- /dev/null
+++ b/oemlock/aidl/aidl_api/android.hardware.oemlock/current/android/hardware/oemlock/OemLockSecureStatus.aidl
@@ -0,0 +1,25 @@
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.oemlock;
+@Backing(type="int") @VintfStability
+enum OemLockSecureStatus {
+  OK = 0,
+  FAILED = 1,
+  INVALID_SIGNATURE = 2,
+}
diff --git a/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl b/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl
new file mode 100644
index 0000000..674ff85
--- /dev/null
+++ b/oemlock/aidl/android/hardware/oemlock/IOemLock.aidl
@@ -0,0 +1,81 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.oemlock;
+
+import android.hardware.oemlock.OemLockSecureStatus;
+
+/*
+ * The OEM lock prevents the bootloader from allowing the device to be flashed.
+ *
+ * Both the carrier and the device itself have a say as to whether OEM unlock is
+ * allowed and both must agree that is allowed in order for unlock to be
+ * possible.
+ */
+@VintfStability
+interface IOemLock {
+    /**
+     * Returns a vendor specific identifier of the HAL.
+     *
+     * The name returned must not be interpreted by the framework but must be
+     * passed to vendor code which may use it to identify the security protocol
+     * used by setOemUnlockAllowedByCarrier. This allows the vendor to identify
+     * the protocol without having to maintain a device-to-protocol mapping.
+     *
+     * @return name of the implementation and STATUS_OK if get name successfully
+     */
+    String getName();
+
+    /**
+     * Returns whether OEM unlock is allowed by the carrier.
+     *
+     * @return the current state(allowed/not allowed) of the flag
+     * and STATUS_OK if the flag was successfully read.
+     */
+    boolean isOemUnlockAllowedByCarrier();
+
+    /**
+     * Returns whether OEM unlock ia allowed by the device.
+     *
+     * @return the current state(allowed/not allowed) of the flag
+     * and STATUS_OK if the flag was successfully read.
+     */
+    boolean isOemUnlockAllowedByDevice();
+
+    /**
+     * Updates whether OEM unlock is allowed by the carrier.
+     *
+     * The implementation may require a vendor defined signature to prove the
+     * validity of this request in order to harden its security.
+     *
+     * @param allowed is the new value of the flag.
+     * @param signature to prove validity of this request or empty if not
+     *        required.
+     * @return OK if the flag was successfully updated,
+     *         INVALID_SIGNATURE if a signature is required but the wrong one
+     *         was provided
+     *         FAILED if the update was otherwise unsuccessful.
+     */
+    OemLockSecureStatus setOemUnlockAllowedByCarrier(in boolean allowed, in byte[] signature);
+
+    /**
+     * Updates whether OEM unlock is allowed by the device.
+     *
+     * @param allowed the new value of the flag.
+     * @return STATUS_OK if the flag was successfully updated.
+     */
+    void setOemUnlockAllowedByDevice(in boolean allowed);
+}
diff --git a/oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl b/oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl
new file mode 100644
index 0000000..3c11377
--- /dev/null
+++ b/oemlock/aidl/android/hardware/oemlock/OemLockSecureStatus.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.oemlock;
+
+@VintfStability
+@Backing(type="int")
+enum OemLockSecureStatus {
+    /**
+     * The operation completed successfully.
+     */
+    OK,
+    /**
+     * The operation encountered a problem.
+     */
+    FAILED,
+    /**
+     * An invalid signature was provided so the operation was not performed.
+     */
+    INVALID_SIGNATURE,
+}
diff --git a/oemlock/aidl/default/Android.bp b/oemlock/aidl/default/Android.bp
new file mode 100644
index 0000000..84136fe
--- /dev/null
+++ b/oemlock/aidl/default/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.oemlock-service.example",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.oemlock-service.example.rc"],
+    vintf_fragments: ["android.hardware.oemlock-service.example.xml"],
+    vendor: true,
+    srcs: [
+        "service.cpp",
+        "OemLock.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.oemlock-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+    ],
+}
diff --git a/oemlock/aidl/default/OemLock.cpp b/oemlock/aidl/default/OemLock.cpp
new file mode 100644
index 0000000..646b532
--- /dev/null
+++ b/oemlock/aidl/default/OemLock.cpp
@@ -0,0 +1,56 @@
+/*
+ * 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 "OemLock.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace oemlock {
+
+// Methods from ::android::hardware::oemlock::IOemLock follow.
+
+::ndk::ScopedAStatus OemLock::getName(std::string *out_name) {
+    (void)out_name;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::setOemUnlockAllowedByCarrier(bool in_allowed, const std::vector<uint8_t> &in_signature, OemLockSecureStatus *_aidl_return) {
+    (void)in_allowed;
+    (void)in_signature;
+    (void)_aidl_return;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::isOemUnlockAllowedByCarrier(bool *out_allowed) {
+    (void)out_allowed;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::setOemUnlockAllowedByDevice(bool in_allowed) {
+    (void)in_allowed;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus OemLock::isOemUnlockAllowedByDevice(bool *out_allowed) {
+    (void)out_allowed;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+} // namespace oemlock
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/oemlock/aidl/default/OemLock.h b/oemlock/aidl/default/OemLock.h
new file mode 100644
index 0000000..b0df414
--- /dev/null
+++ b/oemlock/aidl/default/OemLock.h
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/oemlock/BnOemLock.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace oemlock {
+
+using ::aidl::android::hardware::oemlock::IOemLock;
+using ::aidl::android::hardware::oemlock::OemLockSecureStatus;
+
+struct OemLock : public BnOemLock {
+public:
+    OemLock() = default;
+
+    // Methods from ::android::hardware::oemlock::IOemLock follow.
+    ::ndk::ScopedAStatus getName(std::string* out_name) override;
+    ::ndk::ScopedAStatus isOemUnlockAllowedByCarrier(bool* out_allowed) override;
+    ::ndk::ScopedAStatus isOemUnlockAllowedByDevice(bool* out_allowed) override;
+    ::ndk::ScopedAStatus setOemUnlockAllowedByCarrier(bool in_allowed, const std::vector<uint8_t>& in_signature, OemLockSecureStatus* _aidl_return) override;
+    ::ndk::ScopedAStatus setOemUnlockAllowedByDevice(bool in_allowed) override;
+};
+
+} // namespace oemlock
+} // namespace hardware
+} // namespace android
+} // aidl
diff --git a/oemlock/aidl/default/android.hardware.oemlock-service.example.rc b/oemlock/aidl/default/android.hardware.oemlock-service.example.rc
new file mode 100644
index 0000000..57b79d3
--- /dev/null
+++ b/oemlock/aidl/default/android.hardware.oemlock-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.oemlock_default /vendor/bin/hw/android.hardware.oemlock-service.example
+    class hal
+    user hsm
+    group hsm
diff --git a/oemlock/aidl/default/android.hardware.oemlock-service.example.xml b/oemlock/aidl/default/android.hardware.oemlock-service.example.xml
new file mode 100644
index 0000000..b9f137f
--- /dev/null
+++ b/oemlock/aidl/default/android.hardware.oemlock-service.example.xml
@@ -0,0 +1,9 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.oemlock</name>
+        <interface>
+            <name>IOemLock</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/oemlock/aidl/default/service.cpp b/oemlock/aidl/default/service.cpp
new file mode 100644
index 0000000..af828a0
--- /dev/null
+++ b/oemlock/aidl/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "OemLock.h"
+
+using ::aidl::android::hardware::oemlock::OemLock;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<OemLock> oemlock = ndk::SharedRefBase::make<OemLock>();
+
+    const std::string instance = std::string() + OemLock::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(oemlock->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return -1; // Should never be reached
+}
diff --git a/oemlock/aidl/vts/Android.bp b/oemlock/aidl/vts/Android.bp
new file mode 100644
index 0000000..840d20a
--- /dev/null
+++ b/oemlock/aidl/vts/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalOemLockTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalOemLockTargetTest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libbase",
+    ],
+    static_libs: ["android.hardware.oemlock-V1-ndk_platform"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/oemlock/aidl/vts/OWNERS b/oemlock/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/oemlock/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp b/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp
new file mode 100644
index 0000000..6bf6298
--- /dev/null
+++ b/oemlock/aidl/vts/VtsHalOemLockTargetTest.cpp
@@ -0,0 +1,165 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/oemlock/IOemLock.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using ::aidl::android::hardware::oemlock::IOemLock;
+using ::aidl::android::hardware::oemlock::OemLockSecureStatus;
+
+using ndk::SpAIBinder;
+
+struct OemLockAidlTest : public ::testing::TestWithParam<std::string> {
+    virtual void SetUp() override {
+        oemlock = IOemLock::fromBinder(
+            SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(oemlock, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    std::shared_ptr<IOemLock> oemlock;
+};
+
+/*
+ * Check the name can be retrieved
+ */
+TEST_P(OemLockAidlTest, GetName) {
+    std::string name;
+
+    const auto ret = oemlock->getName(&name);
+
+    ASSERT_TRUE(ret.isOk());
+    // Any value acceptable
+};
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_P(OemLockAidlTest, QueryUnlockAllowedByDevice) {
+    bool allowed;
+
+    const auto ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+
+    ASSERT_TRUE(ret.isOk());
+    // Any value acceptable
+}
+
+/*
+ * Check unlock allowed by device state can be toggled
+ */
+TEST_P(OemLockAidlTest, AllowedByDeviceCanBeToggled) {
+    bool allowed;
+
+    // Get the original state so it can be restored
+    const auto get_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+    ASSERT_TRUE(get_ret.isOk());
+    const bool originallyAllowed = allowed;
+
+    // Toggle the state
+    const auto set_ret = oemlock->setOemUnlockAllowedByDevice(!originallyAllowed);
+    ASSERT_TRUE(set_ret.isOk());
+
+    const auto check_set_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+    ASSERT_TRUE(check_set_ret.isOk());
+    ASSERT_EQ(allowed, !originallyAllowed);
+
+    // Restore the state
+    const auto restore_ret = oemlock->setOemUnlockAllowedByDevice(originallyAllowed);
+    ASSERT_TRUE(restore_ret.isOk());
+
+    const auto check_restore_ret = oemlock->isOemUnlockAllowedByDevice(&allowed);
+    ASSERT_TRUE(check_restore_ret.isOk());
+    ASSERT_EQ(allowed, originallyAllowed);
+}
+
+/*
+ * Check the unlock allowed by device state can be queried
+ */
+TEST_P(OemLockAidlTest, QueryUnlockAllowedByCarrier) {
+    bool allowed;
+
+    const auto ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+
+    ASSERT_TRUE(ret.isOk());
+    // Any value acceptable
+}
+
+/*
+ * Attempt to check unlock allowed by carrier can be toggled
+ *
+ * The implementation may involve a signature which cannot be tested here. That
+ * is a valid implementation so the test will pass. If there is no signature
+ * required, the test will toggle the value.
+ */
+TEST_P(OemLockAidlTest, CarrierUnlock) {
+    const std::vector<uint8_t> noSignature = {};
+    bool allowed;
+    OemLockSecureStatus secure_status;
+
+    // Get the original state so it can be restored
+    const auto get_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+    ASSERT_TRUE(get_ret.isOk());
+    const bool originallyAllowed = allowed;
+
+    if (originallyAllowed) {
+       // Only applied to locked devices
+       return;
+    }
+
+    // Toggle the state
+    const auto set_ret = oemlock->setOemUnlockAllowedByCarrier(!originallyAllowed, noSignature, &secure_status);
+    ASSERT_TRUE(set_ret.isOk());
+    ASSERT_NE(secure_status, OemLockSecureStatus::FAILED);
+    const auto set_status = secure_status;
+
+    const auto check_set_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+    ASSERT_TRUE(check_set_ret.isOk());
+
+    if (set_status == OemLockSecureStatus::INVALID_SIGNATURE) {
+       // Signature is required so we cannot toggle the value in the test, but this is allowed
+       ASSERT_EQ(allowed, originallyAllowed);
+       return;
+    }
+
+    ASSERT_EQ(set_status, OemLockSecureStatus::OK);
+    ASSERT_EQ(allowed, !originallyAllowed);
+
+    // Restore the state
+    const auto restore_ret = oemlock->setOemUnlockAllowedByCarrier(originallyAllowed, noSignature, &secure_status);
+    ASSERT_TRUE(restore_ret.isOk());
+    ASSERT_EQ(secure_status, OemLockSecureStatus::OK);
+
+    const auto check_restore_ret = oemlock->isOemUnlockAllowedByCarrier(&allowed);
+    ASSERT_TRUE(check_restore_ret.isOk());
+    ASSERT_EQ(allowed, originallyAllowed);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OemLockAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, OemLockAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IOemLock::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/power/1.0/Android.bp b/power/1.0/Android.bp
index 6ba1d78..68596ef 100644
--- a/power/1.0/Android.bp
+++ b/power/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.power@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IPower.hal",
diff --git a/power/1.0/default/Android.bp b/power/1.0/default/Android.bp
index 1d152ee..a64b15c 100644
--- a/power/1.0/default/Android.bp
+++ b/power/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.power@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/power/1.0/default/Power.cpp b/power/1.0/default/Power.cpp
index 51f87f5..b91a6e8 100644
--- a/power/1.0/default/Power.cpp
+++ b/power/1.0/default/Power.cpp
@@ -35,7 +35,6 @@
 }
 
 Power::~Power() {
-    delete(mModule);
 }
 
 // Methods from ::android::hardware::power::V1_0::IPower follow.
diff --git a/power/1.0/vts/functional/Android.bp b/power/1.0/vts/functional/Android.bp
index 27b9456..7d90a83 100644
--- a/power/1.0/vts/functional/Android.bp
+++ b/power/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp b/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
index 7e0ae9c..d011c30 100644
--- a/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
+++ b/power/1.0/vts/functional/VtsHalPowerV1_0TargetTest.cpp
@@ -59,7 +59,7 @@
   sp<IPower> power;
 };
 
-// Sanity check Power::setInteractive.
+// Validate Power::setInteractive.
 TEST_P(PowerHidlTest, SetInteractive) {
   Return<void> ret;
 
@@ -101,7 +101,7 @@
   ASSERT_TRUE(android::base::WriteStringToFile(old_governor, CPU_GOVERNOR_PATH));
 }
 
-// Sanity check Power::powerHint on good and bad inputs.
+// Validate Power::powerHint on good and bad inputs.
 TEST_P(PowerHidlTest, PowerHint) {
   PowerHint badHint = static_cast<PowerHint>(0xA);
   auto hints = {PowerHint::VSYNC,         PowerHint::INTERACTION,
@@ -139,7 +139,7 @@
   } while (std::next_permutation(hints2.begin(), hints2.end(), compareHints));
 }
 
-// Sanity check Power::setFeature() on good and bad inputs.
+// Validate Power::setFeature() on good and bad inputs.
 TEST_P(PowerHidlTest, SetFeature) {
   Return<void> ret;
   ret = power->setFeature(Feature::POWER_FEATURE_DOUBLE_TAP_TO_WAKE, true);
@@ -154,7 +154,7 @@
   ASSERT_TRUE(ret.isOk());
 }
 
-// Sanity check Power::getPlatformLowPowerStats().
+// Validate Power::getPlatformLowPowerStats().
 TEST_P(PowerHidlTest, GetPlatformLowPowerStats) {
   hidl_vec<PowerStatePlatformSleepState> vec;
   Status s;
@@ -168,6 +168,7 @@
   ASSERT_TRUE(s == Status::SUCCESS || s == Status::FILESYSTEM_ERROR);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, PowerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IPower::descriptor)),
diff --git a/power/1.1/Android.bp b/power/1.1/Android.bp
index 6b133cc..259be42 100644
--- a/power/1.1/Android.bp
+++ b/power/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.power@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IPower.hal",
diff --git a/power/1.1/vts/functional/Android.bp b/power/1.1/vts/functional/Android.bp
index 2860fdb..4270ab7 100644
--- a/power/1.1/vts/functional/Android.bp
+++ b/power/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/power/1.1/vts/functional/VtsHalPowerV1_1TargetTest.cpp b/power/1.1/vts/functional/VtsHalPowerV1_1TargetTest.cpp
index e9a722c..2904a0d 100644
--- a/power/1.1/vts/functional/VtsHalPowerV1_1TargetTest.cpp
+++ b/power/1.1/vts/functional/VtsHalPowerV1_1TargetTest.cpp
@@ -41,7 +41,7 @@
   sp<IPower> power;
 };
 
-// Sanity check Power::getSubsystemLowPowerStats().
+// Validate Power::getSubsystemLowPowerStats().
 TEST_P(PowerHidlTest, GetSubsystemLowPowerStats) {
   hidl_vec<PowerStateSubsystem> vec;
   Status s;
@@ -56,7 +56,7 @@
   ASSERT_TRUE(s == Status::SUCCESS || s == Status::FILESYSTEM_ERROR);
 }
 
-// Sanity check Power::powerHintAsync on good and bad inputs.
+// Validate Power::powerHintAsync on good and bad inputs.
 TEST_P(PowerHidlTest, PowerHintAsync) {
     PowerHint badHint = static_cast<PowerHint>(0xA);
     auto hints = {PowerHint::VSYNC,        PowerHint::INTERACTION, PowerHint::VIDEO_ENCODE,
@@ -91,6 +91,7 @@
     } while (std::next_permutation(hints2.begin(), hints2.end(), compareHints));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, PowerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IPower::descriptor)),
diff --git a/power/1.2/Android.bp b/power/1.2/Android.bp
index 296965b..aad89e0 100644
--- a/power/1.2/Android.bp
+++ b/power/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.power@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IPower.hal",
diff --git a/power/1.2/vts/functional/Android.bp b/power/1.2/vts/functional/Android.bp
index 5d1b2a4..ab4b601 100644
--- a/power/1.2/vts/functional/Android.bp
+++ b/power/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/power/1.2/vts/functional/VtsHalPowerV1_2TargetTest.cpp b/power/1.2/vts/functional/VtsHalPowerV1_2TargetTest.cpp
index a5ecf5d..1c6eba4 100644
--- a/power/1.2/vts/functional/VtsHalPowerV1_2TargetTest.cpp
+++ b/power/1.2/vts/functional/VtsHalPowerV1_2TargetTest.cpp
@@ -37,7 +37,7 @@
     sp<IPower> power;
 };
 
-// Sanity check Power::PowerHintAsync_1_2 on good and bad inputs.
+// Validate Power::PowerHintAsync_1_2 on good and bad inputs.
 TEST_P(PowerHidlTest, PowerHintAsync_1_2) {
     std::vector<PowerHint> hints;
     for (uint32_t i = static_cast<uint32_t>(PowerHint::VSYNC);
@@ -76,6 +76,7 @@
     } while (std::next_permutation(hints2.begin(), hints2.end(), compareHints));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, PowerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IPower::descriptor)),
diff --git a/power/1.3/Android.bp b/power/1.3/Android.bp
index 00ca750..9346797 100644
--- a/power/1.3/Android.bp
+++ b/power/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.power@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IPower.hal",
diff --git a/power/1.3/vts/functional/Android.bp b/power/1.3/vts/functional/Android.bp
index d8e1c05..c1186e3 100644
--- a/power/1.3/vts/functional/Android.bp
+++ b/power/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerV1_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/power/1.3/vts/functional/VtsHalPowerV1_3TargetTest.cpp b/power/1.3/vts/functional/VtsHalPowerV1_3TargetTest.cpp
index 3cf2adc..38f4cf5 100644
--- a/power/1.3/vts/functional/VtsHalPowerV1_3TargetTest.cpp
+++ b/power/1.3/vts/functional/VtsHalPowerV1_3TargetTest.cpp
@@ -41,6 +41,7 @@
     ASSERT_TRUE(power->powerHintAsync_1_3(PowerHint::EXPENSIVE_RENDERING, 0).isOk());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, PowerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IPower::descriptor)),
diff --git a/power/aidl/Android.bp b/power/aidl/Android.bp
index 4008652..054fea5 100644
--- a/power/aidl/Android.bp
+++ b/power/aidl/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.power",
     vendor_available: true,
diff --git a/power/aidl/default/Android.bp b/power/aidl/default/Android.bp
index 07cd368..5aa6893 100644
--- a/power/aidl/default/Android.bp
+++ b/power/aidl/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.power-service.example",
     relative_install_path: "hw",
@@ -21,7 +30,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.power-ndk_platform",
+        "android.hardware.power-V1-ndk_platform",
     ],
     srcs: [
         "main.cpp",
diff --git a/power/aidl/vts/Android.bp b/power/aidl/vts/Android.bp
index 28b08c7..1051b03 100644
--- a/power/aidl/vts/Android.bp
+++ b/power/aidl/vts/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerTargetTest",
     defaults: [
@@ -23,7 +32,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.power-cpp",
+        "android.hardware.power-V1-cpp",
     ],
     test_suites: [
         "vts",
diff --git a/power/aidl/vts/VtsHalPowerTargetTest.cpp b/power/aidl/vts/VtsHalPowerTargetTest.cpp
index 25a385e..d036c90 100644
--- a/power/aidl/vts/VtsHalPowerTargetTest.cpp
+++ b/power/aidl/vts/VtsHalPowerTargetTest.cpp
@@ -126,6 +126,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerAidl);
 INSTANTIATE_TEST_SUITE_P(Power, PowerAidl,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IPower::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/power/stats/1.0/Android.bp b/power/stats/1.0/Android.bp
index c592006..72da04e 100644
--- a/power/stats/1.0/Android.bp
+++ b/power/stats/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.power.stats@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IPowerStats.hal",
diff --git a/power/stats/1.0/default/Android.bp b/power/stats/1.0/default/Android.bp
index 0321da1..f0bbef1 100644
--- a/power/stats/1.0/default/Android.bp
+++ b/power/stats/1.0/default/Android.bp
@@ -11,6 +11,15 @@
 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 // See the License for the specific language governing permissions and
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.power.stats@1.0-service.mock",
     relative_install_path: "hw",
diff --git a/power/stats/1.0/vts/functional/Android.bp b/power/stats/1.0/vts/functional/Android.bp
index d5f1da2..5a448d8 100644
--- a/power/stats/1.0/vts/functional/Android.bp
+++ b/power/stats/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalPowerStatsV1_0TargetTest",
     defaults: [
diff --git a/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp b/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp
index 3359669..c7d8091 100644
--- a/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp
+++ b/power/stats/1.0/vts/functional/VtsHalPowerStatsV1_0TargetTest.cpp
@@ -556,6 +556,7 @@
     thread1.join();
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerStatsHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, PowerStatsHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IPowerStats::descriptor)),
diff --git a/power/stats/aidl/Android.bp b/power/stats/aidl/Android.bp
new file mode 100644
index 0000000..0dbf9b4
--- /dev/null
+++ b/power/stats/aidl/Android.bp
@@ -0,0 +1,45 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.power.stats",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/power/stats/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+        cpp: {
+            enabled: true,
+        },
+    },
+    host_supported: true,
+}
diff --git a/power/stats/aidl/OWNERS b/power/stats/aidl/OWNERS
new file mode 100644
index 0000000..b290b49
--- /dev/null
+++ b/power/stats/aidl/OWNERS
@@ -0,0 +1,3 @@
+bsschwar@google.com
+krossmo@google.com
+tstrudel@google.com
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/Channel.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/Channel.aidl
new file mode 100644
index 0000000..e0c372b
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/Channel.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable Channel {
+  int id;
+  @utf8InCpp String name;
+  @utf8InCpp String subsystem;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl
new file mode 100644
index 0000000..cd9239e
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumer.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable EnergyConsumer {
+  int id;
+  int ordinal;
+  android.hardware.power.stats.EnergyConsumerType type = android.hardware.power.stats.EnergyConsumerType.OTHER;
+  @utf8InCpp String name;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerAttribution.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerAttribution.aidl
new file mode 100644
index 0000000..2fdc1a5
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerAttribution.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable EnergyConsumerAttribution {
+  int uid;
+  long energyUWs;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerResult.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerResult.aidl
new file mode 100644
index 0000000..4937aef
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerResult.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable EnergyConsumerResult {
+  int id;
+  long timestampMs;
+  long energyUWs;
+  android.hardware.power.stats.EnergyConsumerAttribution[] attribution;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl
new file mode 100644
index 0000000..ce3e1f5
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyConsumerType.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+enum EnergyConsumerType {
+  OTHER = 0,
+  BLUETOOTH = 1,
+  CPU_CLUSTER = 2,
+  DISPLAY = 3,
+  GNSS = 4,
+  MOBILE_RADIO = 5,
+  WIFI = 6,
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyMeasurement.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyMeasurement.aidl
new file mode 100644
index 0000000..2d6ae16
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/EnergyMeasurement.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable EnergyMeasurement {
+  int id;
+  long timestampMs;
+  long durationMs;
+  long energyUWs;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/IPowerStats.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/IPowerStats.aidl
new file mode 100644
index 0000000..5b11695
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/IPowerStats.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+interface IPowerStats {
+  android.hardware.power.stats.PowerEntity[] getPowerEntityInfo();
+  android.hardware.power.stats.StateResidencyResult[] getStateResidency(in int[] powerEntityIds);
+  android.hardware.power.stats.EnergyConsumer[] getEnergyConsumerInfo();
+  android.hardware.power.stats.EnergyConsumerResult[] getEnergyConsumed(in int[] energyConsumerIds);
+  android.hardware.power.stats.Channel[] getEnergyMeterInfo();
+  android.hardware.power.stats.EnergyMeasurement[] readEnergyMeter(in int[] channelIds);
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/PowerEntity.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/PowerEntity.aidl
new file mode 100644
index 0000000..38a62ac
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/PowerEntity.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable PowerEntity {
+  int id;
+  @utf8InCpp String name;
+  android.hardware.power.stats.State[] states;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/State.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/State.aidl
new file mode 100644
index 0000000..1c5d7c1
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/State.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable State {
+  int id;
+  @utf8InCpp String name;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidency.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidency.aidl
new file mode 100644
index 0000000..7982f02
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidency.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable StateResidency {
+  int id;
+  long totalTimeInStateMs;
+  long totalStateEntryCount;
+  long lastEntryTimestampMs;
+}
diff --git a/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidencyResult.aidl b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidencyResult.aidl
new file mode 100644
index 0000000..5bc548e
--- /dev/null
+++ b/power/stats/aidl/aidl_api/android.hardware.power.stats/current/android/hardware/power/stats/StateResidencyResult.aidl
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *////////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.power.stats;
+@VintfStability
+parcelable StateResidencyResult {
+  int id;
+  android.hardware.power.stats.StateResidency[] stateResidencyData;
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/Channel.aidl b/power/stats/aidl/android/hardware/power/stats/Channel.aidl
new file mode 100644
index 0000000..5e8962e
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/Channel.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+@VintfStability
+parcelable Channel {
+    /**
+     * Unique ID of this Channel
+     */
+    int id;
+    /**
+     * Unique name of this Channel. Vendor/device specific. Opaque to framework
+     */
+    @utf8InCpp String name;
+
+    /**
+     * Name of the subsystem associated with this Channel. Opaque to framework
+     */
+    @utf8InCpp String subsystem;
+}
+
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl
new file mode 100644
index 0000000..ec616f2
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumer.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerType;
+
+@VintfStability
+parcelable EnergyConsumer {
+    /**
+     * Unique ID of this EnergyConsumer
+     */
+    int id;
+
+    /**
+     * For a group of EnergyConsumers of the same logical type, sorting by ordinal
+     * gives their physical order. Ordinals must be consecutive integers starting from 0.
+     */
+    int ordinal;
+
+    /* Type of this EnergyConsumer */
+    EnergyConsumerType type = EnergyConsumerType.OTHER;
+
+    /**
+     * Unique name of this EnergyConsumer. Vendor/device specific. Opaque to framework
+     */
+    @utf8InCpp String name;
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl
new file mode 100644
index 0000000..5767de1
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerAttribution.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+@VintfStability
+parcelable EnergyConsumerAttribution {
+    /**
+     * Android ID / Linux UID, the accumulated energy is attributed to
+     */
+    int uid;
+    /**
+     * Accumulated energy since boot in microwatt-seconds (uWs) for this AID
+     */
+    long energyUWs;
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl
new file mode 100644
index 0000000..66c8c8c
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerResult.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+import android.hardware.power.stats.EnergyConsumerAttribution;
+
+@VintfStability
+parcelable EnergyConsumerResult {
+    /**
+     * ID of the EnergyConsumer associated with this result
+     */
+    int id;
+    /**
+     * Time of data capture in milliseconds since boot (CLOCK_BOOTTIME clock)
+     */
+    long timestampMs;
+    /**
+     * Total accumulated energy since boot in microwatt-seconds (uWs)
+     */
+    long energyUWs;
+    /**
+     * Optional attributed energy per Android ID / Linux UID for this EnergyConsumer.
+     * Sum total of attributed energy must be less than or equal to total accumulated energy.
+     */
+    EnergyConsumerAttribution[] attribution;
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
new file mode 100644
index 0000000..d871ced
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyConsumerType.aidl
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+/* Indicates the type of an energy consumer reported by the Power Stats HAL */
+@VintfStability
+enum EnergyConsumerType {
+    OTHER,
+    BLUETOOTH,
+    CPU_CLUSTER,
+    DISPLAY,
+    GNSS,
+    MOBILE_RADIO,
+    WIFI,
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl b/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl
new file mode 100644
index 0000000..31fbaa8
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/EnergyMeasurement.aidl
@@ -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.
+ */
+
+package android.hardware.power.stats;
+
+@VintfStability
+parcelable EnergyMeasurement {
+    /**
+     * ID of the Channel associated with this measurement
+     */
+    int id;
+    /**
+     * Time of data capture in milliseconds since boot (CLOCK_BOOTTIME clock)
+     */
+    long timestampMs;
+    /**
+     * Duration in milliseconds that energy has been accumulated
+     */
+    long durationMs;
+    /**
+     * Accumulated energy in microwatt-seconds (uWs)
+     */
+    long energyUWs;
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl b/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl
new file mode 100644
index 0000000..edc43ea
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/IPowerStats.aidl
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+import android.hardware.power.stats.Channel;
+import android.hardware.power.stats.EnergyConsumer;
+import android.hardware.power.stats.EnergyConsumerResult;
+import android.hardware.power.stats.EnergyMeasurement;
+import android.hardware.power.stats.PowerEntity;
+import android.hardware.power.stats.StateResidencyResult;
+
+@VintfStability
+interface IPowerStats {
+    /**
+     * Return information related to all supported PowerEntity(s) for which state residency data
+     * is available.
+     *
+     * A PowerEntity is defined as a platform subsystem, peripheral, or power domain that impacts
+     * the total device power consumption.
+     *
+     * @return List of information on each PowerEntity for which state residency can be requested.
+     */
+    PowerEntity[] getPowerEntityInfo();
+
+    /**
+     * Reports the accumulated state residency for each requested PowerEntity.
+     *
+     * Each PowerEntity may reside in one of multiple states. It may also
+     * transition from one state to another. StateResidency is defined as
+     * an accumulation of time that a PowerEntity resided in each
+     * of its possible states, the number of times that each state was
+     * entered, and a timestamp corresponding to the last time that state
+     * was entered.
+     *
+     * Data is accumulated starting at device boot.
+     *
+     * @param powerEntityIds List of IDs of PowerEntities for which data is requested.
+     *     Passing an empty list will return state residency for all available PowerEntitys.
+     *     ID of each PowerEntity is contained in PowerEntityInfo.
+     *
+     * @return StateResidencyResults since boot for each requested and available PowerEntity. Note
+     * that StateResidencyResult for a given PowerEntity may not always be available. Clients shall
+     * not rely on StateResidencyResult always being returned for every request.
+     *
+     * Returns the following exception codes:
+     *  - EX_ILLEGAL_ARGUMENT if an invalid powerEntityId is provided
+     */
+    StateResidencyResult[] getStateResidency(in int[] powerEntityIds);
+
+    /**
+     * Return the list EnergyConsumers for which energy consumption data is available.
+     *
+     * An EnergyConsumer is a device subsystem or peripheral that consumes energy. Energy
+     * consumption data may be used by framework for the purpose of power attribution.
+     *
+     * @return List of EnergyConsumers for which energy consumption can be requested.
+     */
+    EnergyConsumer[] getEnergyConsumerInfo();
+
+    /**
+     * Reports the energy consumed since boot by each requested EnergyConsumer.
+     *
+     * @param energyConsumerIds List of IDs of EnergyConsumers for which data is requested.
+     *     Passing an empty list will return results for all available EnergyConsumers.
+     *
+     * @return Energy consumed since boot for each requested and available EnergyConsumer. Note
+     * that EnergyConsumerResult for a given EnergyConsumer may not always be available. Clients
+     * shall not rely on EnergyConsumerResult always being returned for every request.
+     *
+     * Returns the following exception codes:
+     *  - EX_ILLEGAL_ARGUMENT if an invalid energyConsumerId is provided
+     */
+    EnergyConsumerResult[] getEnergyConsumed(in int[] energyConsumerIds);
+
+    /**
+     * Return information related to all Channels monitored by Energy Meters.
+     *
+     * An Energy Meter is a device that monitors energy and may support monitoring multiple
+     * channels simultaneously. A channel may correspond a bus, sense resistor, or power rail.
+     *
+     * @return All Channels for which energy measurements can be requested.
+     */
+    Channel[] getEnergyMeterInfo();
+
+    /**
+     * Reports accumulated energy for each specified Channel.
+     *
+     * @param channelIds IDs of channels for which data is requested.
+     *     Passing an empty list will return energy measurements for all available channels.
+     *     ID of each channel is contained in ChannelInfo.
+     *
+     * @return Energy measured since boot for each requested and available Channel. Note
+     * that EnergyMeasurement for a given Channel may not always be available. Clients
+     * shall not rely on EnergyMeasurement always being returned for every request.
+     *
+     * Returns the following exception codes:
+     *  - EX_ILLEGAL_ARGUMENT if an invalid channelId is provided
+     */
+    EnergyMeasurement[] readEnergyMeter(in int[] channelIds);
+}
diff --git a/power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl b/power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl
new file mode 100644
index 0000000..6844a4c
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/PowerEntity.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+import android.hardware.power.stats.State;
+
+@VintfStability
+parcelable PowerEntity {
+    /**
+     * Unique ID of this PowerEntity
+     */
+    int id;
+    /**
+     * Unique name of this PowerEntity. Vendor/device specific. Opaque to framework
+     */
+    @utf8InCpp String name;
+    /**
+     * List of states that this PowerEntity may reside in
+     */
+    State[] states;
+}
\ No newline at end of file
diff --git a/power/stats/aidl/android/hardware/power/stats/State.aidl b/power/stats/aidl/android/hardware/power/stats/State.aidl
new file mode 100644
index 0000000..33a9f70
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/State.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+@VintfStability
+parcelable State {
+    /**
+     * Unique (for a given PowerEntity) ID of this State
+     */
+    int id;
+    /**
+     * Unique (for a given PowerEntity) name of the state. Vendor/device specific.
+     * Opaque to framework
+     */
+    @utf8InCpp String name;
+}
+
diff --git a/power/stats/aidl/android/hardware/power/stats/StateResidency.aidl b/power/stats/aidl/android/hardware/power/stats/StateResidency.aidl
new file mode 100644
index 0000000..4162517
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/StateResidency.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+/**
+ * Contains residency data for a single state
+ */
+@VintfStability
+parcelable StateResidency {
+    /**
+     * ID of the state associated with this residency
+     */
+    int id;
+    /**
+     * Total time in milliseconds that the corresponding PowerEntity resided
+     * in this state since boot
+     */
+    long totalTimeInStateMs;
+    /**
+     * Total number of times that the state was entered since boot
+     */
+    long totalStateEntryCount;
+    /**
+     * Last time this state was entered. Time in milliseconds since boot
+     */
+    long lastEntryTimestampMs;
+}
\ No newline at end of file
diff --git a/power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl b/power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl
new file mode 100644
index 0000000..949879c
--- /dev/null
+++ b/power/stats/aidl/android/hardware/power/stats/StateResidencyResult.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.power.stats;
+
+import android.hardware.power.stats.StateResidency;
+
+@VintfStability
+parcelable StateResidencyResult {
+    /**
+     * ID of the PowerEntity associated with this result
+     */
+    int id;
+    /**
+     * Residency for each state in the PowerEntity's state space
+     */
+    StateResidency[] stateResidencyData;
+}
+
diff --git a/power/stats/aidl/default/Android.bp b/power/stats/aidl/default/Android.bp
new file mode 100644
index 0000000..417dc97
--- /dev/null
+++ b/power/stats/aidl/default/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.power.stats-service.example",
+    relative_install_path: "hw",
+    init_rc: ["power.stats-default.rc"],
+    vintf_fragments: ["power.stats-default.xml"],
+    vendor: true,
+    shared_libs: [
+        "libbase",
+        "libbinder_ndk",
+        "android.hardware.power.stats-V1-ndk_platform",
+    ],
+    srcs: [
+        "main.cpp",
+        "PowerStats.cpp",
+    ],
+}
diff --git a/power/stats/aidl/default/FakeEnergyConsumer.h b/power/stats/aidl/default/FakeEnergyConsumer.h
new file mode 100644
index 0000000..f41aa6e
--- /dev/null
+++ b/power/stats/aidl/default/FakeEnergyConsumer.h
@@ -0,0 +1,83 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <PowerStats.h>
+
+#include <android-base/chrono_utils.h>
+
+#include <chrono>
+#include <random>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class FakeEnergyConsumer : public PowerStats::IEnergyConsumer {
+  public:
+    FakeEnergyConsumer(EnergyConsumerType type, std::string name) : mType(type), mName(name) {
+        mResult.timestampMs = 0;
+        mResult.energyUWs = 0;
+        mResult.attribution = {};
+    }
+
+    ~FakeEnergyConsumer() = default;
+
+    std::string getName() override { return mName; }
+
+    EnergyConsumerType getType() override { return mType; }
+
+    std::optional<EnergyConsumerResult> getEnergyConsumed() override {
+        mFakeEnergyConsumerResult.update(&mResult);
+        return mResult;
+    }
+
+  private:
+    class FakeEnergyConsumerResult {
+      public:
+        FakeEnergyConsumerResult() : mDistribution(1, 100) {}
+        void update(EnergyConsumerResult* result) {
+            // generates number in the range 1..100
+            auto randNum = std::bind(mDistribution, mGenerator);
+
+            // Get current time since boot in milliseconds
+            uint64_t now = std::chrono::time_point_cast<std::chrono::milliseconds>(
+                                   ::android::base::boot_clock::now())
+                                   .time_since_epoch()
+                                   .count();
+            result->timestampMs = now;
+            result->energyUWs += randNum() * 100;
+        }
+
+      private:
+        std::default_random_engine mGenerator;
+        std::uniform_int_distribution<int> mDistribution;
+    };
+
+    EnergyConsumerType mType;
+    std::string mName;
+    FakeEnergyConsumerResult mFakeEnergyConsumerResult;
+    EnergyConsumerResult mResult;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
\ No newline at end of file
diff --git a/power/stats/aidl/default/FakeEnergyMeter.h b/power/stats/aidl/default/FakeEnergyMeter.h
new file mode 100644
index 0000000..56dcdcc
--- /dev/null
+++ b/power/stats/aidl/default/FakeEnergyMeter.h
@@ -0,0 +1,112 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <PowerStats.h>
+
+#include <android-base/chrono_utils.h>
+
+#include <chrono>
+#include <random>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class FakeEnergyMeter : public PowerStats::IEnergyMeter {
+  public:
+    FakeEnergyMeter(std::vector<std::pair<std::string, std::string>> channelNames) {
+        int32_t channelId = 0;
+        for (const auto& [name, subsystem] : channelNames) {
+            Channel c;
+            c.id = channelId++;
+            c.name = name;
+            c.subsystem = subsystem;
+
+            EnergyMeasurement m;
+            m.id = c.id;
+            m.timestampMs = 0;
+            m.durationMs = 0;
+            m.energyUWs = 0;
+
+            mChannels.push_back(c);
+            mEnergyMeasurements.push_back(m);
+        }
+    }
+    ~FakeEnergyMeter() = default;
+    ndk::ScopedAStatus readEnergyMeter(const std::vector<int32_t>& in_channelIds,
+                                       std::vector<EnergyMeasurement>* _aidl_return) override {
+        for (auto& measurement : mEnergyMeasurements) {
+            mFakeEnergyMeasurement.update(&measurement);
+        }
+
+        if (in_channelIds.empty()) {
+            *_aidl_return = mEnergyMeasurements;
+        } else {
+            for (int32_t id : in_channelIds) {
+                // check for invalid ids
+                if (id < 0 || id >= mEnergyMeasurements.size()) {
+                    return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+                }
+
+                _aidl_return->push_back(mEnergyMeasurements[id]);
+            }
+        }
+
+        return ndk::ScopedAStatus::ok();
+    }
+
+    ndk::ScopedAStatus getEnergyMeterInfo(std::vector<Channel>* _aidl_return) override {
+        *_aidl_return = mChannels;
+        return ndk::ScopedAStatus::ok();
+    }
+
+  private:
+    class FakeEnergyMeasurement {
+      public:
+        FakeEnergyMeasurement() : mDistribution(1, 100) {}
+        void update(EnergyMeasurement* measurement) {
+            // generates number in the range 1..100
+            auto randNum = std::bind(mDistribution, mGenerator);
+
+            // Get current time since boot in milliseconds
+            uint64_t now = std::chrono::time_point_cast<std::chrono::milliseconds>(
+                                   ::android::base::boot_clock::now())
+                                   .time_since_epoch()
+                                   .count();
+            measurement->timestampMs = now;
+            measurement->durationMs = now;
+            measurement->energyUWs += randNum() * 100;
+        }
+
+      private:
+        std::default_random_engine mGenerator;
+        std::uniform_int_distribution<int> mDistribution;
+    };
+
+    std::vector<Channel> mChannels;
+    FakeEnergyMeasurement mFakeEnergyMeasurement;
+    std::vector<EnergyMeasurement> mEnergyMeasurements;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
\ No newline at end of file
diff --git a/power/stats/aidl/default/FakeStateResidencyDataProvider.h b/power/stats/aidl/default/FakeStateResidencyDataProvider.h
new file mode 100644
index 0000000..2eeab61
--- /dev/null
+++ b/power/stats/aidl/default/FakeStateResidencyDataProvider.h
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <PowerStats.h>
+
+#include <random>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class FakeStateResidencyDataProvider : public PowerStats::IStateResidencyDataProvider {
+  public:
+    FakeStateResidencyDataProvider(const std::string& name, std::vector<State> states)
+        : mName(name), mStates(states) {
+        for (const auto& state : mStates) {
+            StateResidency r;
+            r.id = state.id;
+            r.totalTimeInStateMs = 0;
+            r.totalStateEntryCount = 0;
+            r.lastEntryTimestampMs = 0;
+            mResidencies.push_back(r);
+        }
+    }
+    ~FakeStateResidencyDataProvider() = default;
+
+    // Methods from PowerStats::IStateResidencyDataProvider
+    bool getStateResidencies(
+            std::unordered_map<std::string, std::vector<StateResidency>>* residencies) override {
+        for (auto& residency : mResidencies) {
+            mFakeStateResidency.update(&residency);
+        }
+
+        residencies->emplace(mName, mResidencies);
+        return true;
+    }
+
+    std::unordered_map<std::string, std::vector<State>> getInfo() override {
+        return {{mName, mStates}};
+    }
+
+  private:
+    class FakeStateResidency {
+      public:
+        FakeStateResidency() : mDistribution(1, 100) {}
+        void update(StateResidency* residency) {
+            // generates number in the range 1..100
+            auto randNum = std::bind(mDistribution, mGenerator);
+
+            residency->totalTimeInStateMs += randNum() * 100;
+            residency->totalStateEntryCount += randNum();
+            residency->lastEntryTimestampMs += randNum() * 100;
+        }
+
+      private:
+        std::default_random_engine mGenerator;
+        std::uniform_int_distribution<int> mDistribution;
+    };
+
+    const std::string mName;
+    const std::vector<State> mStates;
+    FakeStateResidency mFakeStateResidency;
+    std::vector<StateResidency> mResidencies;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
\ No newline at end of file
diff --git a/power/stats/aidl/default/PowerStats.cpp b/power/stats/aidl/default/PowerStats.cpp
new file mode 100644
index 0000000..4b771a8
--- /dev/null
+++ b/power/stats/aidl/default/PowerStats.cpp
@@ -0,0 +1,179 @@
+/*
+ * 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 "PowerStats.h"
+
+#include <android-base/logging.h>
+
+#include <numeric>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+void PowerStats::addStateResidencyDataProvider(std::unique_ptr<IStateResidencyDataProvider> p) {
+    if (!p) {
+        return;
+    }
+
+    int32_t id = mPowerEntityInfos.size();
+    auto info = p->getInfo();
+
+    size_t index = mStateResidencyDataProviders.size();
+    mStateResidencyDataProviders.emplace_back(std::move(p));
+
+    for (const auto& [entityName, states] : info) {
+        PowerEntity i = {
+                .id = id++,
+                .name = entityName,
+                .states = states,
+        };
+        mPowerEntityInfos.emplace_back(i);
+        mStateResidencyDataProviderIndex.emplace_back(index);
+    }
+}
+
+void PowerStats::addEnergyConsumer(std::unique_ptr<IEnergyConsumer> p) {
+    if (!p) {
+        return;
+    }
+
+    EnergyConsumerType type = p->getType();
+    std::string name = p->getName();
+    int32_t count = count_if(mEnergyConsumerInfos.begin(), mEnergyConsumerInfos.end(),
+                             [&type](const EnergyConsumer& c) { return type == c.type; });
+    int32_t id = mEnergyConsumers.size();
+    mEnergyConsumerInfos.emplace_back(
+            EnergyConsumer{.id = id, .ordinal = count, .type = type, .name = name});
+    mEnergyConsumers.emplace_back(std::move(p));
+}
+
+void PowerStats::setEnergyMeter(std::unique_ptr<IEnergyMeter> p) {
+    mEnergyMeter = std::move(p);
+}
+
+ndk::ScopedAStatus PowerStats::getPowerEntityInfo(std::vector<PowerEntity>* _aidl_return) {
+    *_aidl_return = mPowerEntityInfos;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerStats::getStateResidency(const std::vector<int32_t>& in_powerEntityIds,
+                                                 std::vector<StateResidencyResult>* _aidl_return) {
+    if (mPowerEntityInfos.empty()) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    // If in_powerEntityIds is empty then return data for all supported entities
+    if (in_powerEntityIds.empty()) {
+        std::vector<int32_t> v(mPowerEntityInfos.size());
+        std::iota(std::begin(v), std::end(v), 0);
+        return getStateResidency(v, _aidl_return);
+    }
+
+    std::unordered_map<std::string, std::vector<StateResidency>> stateResidencies;
+
+    for (const int32_t id : in_powerEntityIds) {
+        // check for invalid ids
+        if (id < 0 || id >= mPowerEntityInfos.size()) {
+            return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+        }
+
+        // Check to see if we already have data for the given id
+        std::string powerEntityName = mPowerEntityInfos[id].name;
+        if (stateResidencies.find(powerEntityName) == stateResidencies.end()) {
+            mStateResidencyDataProviders.at(mStateResidencyDataProviderIndex.at(id))
+                    ->getStateResidencies(&stateResidencies);
+        }
+
+        // Append results if we have them
+        auto stateResidency = stateResidencies.find(powerEntityName);
+        if (stateResidency != stateResidencies.end()) {
+            StateResidencyResult res = {
+                    .id = id,
+                    .stateResidencyData = stateResidency->second,
+            };
+            _aidl_return->emplace_back(res);
+        } else {
+            // Failed to get results for the given id.
+            LOG(ERROR) << "Failed to get results for " << powerEntityName;
+        }
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerStats::getEnergyConsumerInfo(std::vector<EnergyConsumer>* _aidl_return) {
+    *_aidl_return = mEnergyConsumerInfos;
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerStats::getEnergyConsumed(const std::vector<int32_t>& in_energyConsumerIds,
+                                                 std::vector<EnergyConsumerResult>* _aidl_return) {
+    if (mEnergyConsumers.empty()) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    // If in_powerEntityIds is empty then return data for all supported energy consumers
+    if (in_energyConsumerIds.empty()) {
+        std::vector<int32_t> v(mEnergyConsumerInfos.size());
+        std::iota(std::begin(v), std::end(v), 0);
+        return getEnergyConsumed(v, _aidl_return);
+    }
+
+    for (const auto id : in_energyConsumerIds) {
+        // check for invalid ids
+        if (id < 0 || id >= mEnergyConsumers.size()) {
+            return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_ILLEGAL_ARGUMENT));
+        }
+
+        auto optionalResult = mEnergyConsumers[id]->getEnergyConsumed();
+        if (optionalResult) {
+            EnergyConsumerResult result = optionalResult.value();
+            result.id = id;
+            _aidl_return->emplace_back(result);
+        } else {
+            // Failed to get results for the given id.
+            LOG(ERROR) << "Failed to get results for " << mEnergyConsumerInfos[id].name;
+        }
+    }
+
+    return ndk::ScopedAStatus::ok();
+}
+
+ndk::ScopedAStatus PowerStats::getEnergyMeterInfo(std::vector<Channel>* _aidl_return) {
+    if (!mEnergyMeter) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    return mEnergyMeter->getEnergyMeterInfo(_aidl_return);
+}
+
+ndk::ScopedAStatus PowerStats::readEnergyMeter(const std::vector<int32_t>& in_channelIds,
+                                               std::vector<EnergyMeasurement>* _aidl_return) {
+    if (!mEnergyMeter) {
+        return ndk::ScopedAStatus::ok();
+    }
+
+    return mEnergyMeter->readEnergyMeter(in_channelIds, _aidl_return);
+}
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/power/stats/aidl/default/PowerStats.h b/power/stats/aidl/default/PowerStats.h
new file mode 100644
index 0000000..91d272d
--- /dev/null
+++ b/power/stats/aidl/default/PowerStats.h
@@ -0,0 +1,89 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/power/stats/BnPowerStats.h>
+
+#include <unordered_map>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace power {
+namespace stats {
+
+class PowerStats : public BnPowerStats {
+  public:
+    class IStateResidencyDataProvider {
+      public:
+        virtual ~IStateResidencyDataProvider() = default;
+        virtual bool getStateResidencies(
+                std::unordered_map<std::string, std::vector<StateResidency>>* residencies) = 0;
+        virtual std::unordered_map<std::string, std::vector<State>> getInfo() = 0;
+    };
+
+    class IEnergyConsumer {
+      public:
+        virtual ~IEnergyConsumer() = default;
+        virtual std::string getName() = 0;
+        virtual EnergyConsumerType getType() = 0;
+        virtual std::optional<EnergyConsumerResult> getEnergyConsumed() = 0;
+    };
+
+    class IEnergyMeter {
+      public:
+        virtual ~IEnergyMeter() = default;
+        virtual ndk::ScopedAStatus readEnergyMeter(
+                const std::vector<int32_t>& in_channelIds,
+                std::vector<EnergyMeasurement>* _aidl_return) = 0;
+        virtual ndk::ScopedAStatus getEnergyMeterInfo(std::vector<Channel>* _aidl_return) = 0;
+    };
+
+    PowerStats() = default;
+
+    void addStateResidencyDataProvider(std::unique_ptr<IStateResidencyDataProvider> p);
+    void addEnergyConsumer(std::unique_ptr<IEnergyConsumer> p);
+    void setEnergyMeter(std::unique_ptr<IEnergyMeter> p);
+
+    // Methods from aidl::android::hardware::power::stats::IPowerStats
+    ndk::ScopedAStatus getPowerEntityInfo(std::vector<PowerEntity>* _aidl_return) override;
+    ndk::ScopedAStatus getStateResidency(const std::vector<int32_t>& in_powerEntityIds,
+                                         std::vector<StateResidencyResult>* _aidl_return) override;
+    ndk::ScopedAStatus getEnergyConsumerInfo(std::vector<EnergyConsumer>* _aidl_return) override;
+    ndk::ScopedAStatus getEnergyConsumed(const std::vector<int32_t>& in_energyConsumerIds,
+                                         std::vector<EnergyConsumerResult>* _aidl_return) override;
+    ndk::ScopedAStatus getEnergyMeterInfo(std::vector<Channel>* _aidl_return) override;
+    ndk::ScopedAStatus readEnergyMeter(const std::vector<int32_t>& in_channelIds,
+                                       std::vector<EnergyMeasurement>* _aidl_return) override;
+
+  private:
+    std::vector<std::unique_ptr<IStateResidencyDataProvider>> mStateResidencyDataProviders;
+    std::vector<PowerEntity> mPowerEntityInfos;
+    /* Index that maps each power entity id to an entry in mStateResidencyDataProviders */
+    std::vector<size_t> mStateResidencyDataProviderIndex;
+
+    std::vector<std::unique_ptr<IEnergyConsumer>> mEnergyConsumers;
+    std::vector<EnergyConsumer> mEnergyConsumerInfos;
+
+    std::unique_ptr<IEnergyMeter> mEnergyMeter;
+};
+
+}  // namespace stats
+}  // namespace power
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/power/stats/aidl/default/main.cpp b/power/stats/aidl/default/main.cpp
new file mode 100644
index 0000000..2fe3d2e
--- /dev/null
+++ b/power/stats/aidl/default/main.cpp
@@ -0,0 +1,80 @@
+/*
+ * 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 "PowerStats.h"
+
+#include "FakeEnergyConsumer.h"
+#include "FakeEnergyMeter.h"
+#include "FakeStateResidencyDataProvider.h"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+using aidl::android::hardware::power::stats::EnergyConsumerType;
+using aidl::android::hardware::power::stats::FakeEnergyConsumer;
+using aidl::android::hardware::power::stats::FakeEnergyMeter;
+using aidl::android::hardware::power::stats::FakeStateResidencyDataProvider;
+using aidl::android::hardware::power::stats::PowerStats;
+using aidl::android::hardware::power::stats::State;
+
+void setFakeEnergyMeter(std::shared_ptr<PowerStats> p) {
+    p->setEnergyMeter(
+            std::make_unique<FakeEnergyMeter>(std::vector<std::pair<std::string, std::string>>{
+                    {"Rail1", "Display"},
+                    {"Rail2", "CPU"},
+                    {"Rail3", "Modem"},
+            }));
+}
+
+void addFakeStateResidencyDataProvider1(std::shared_ptr<PowerStats> p) {
+    p->addStateResidencyDataProvider(std::make_unique<FakeStateResidencyDataProvider>(
+            "CPU", std::vector<State>{{0, "Idle"}, {1, "Active"}}));
+}
+
+void addFakeStateResidencyDataProvider2(std::shared_ptr<PowerStats> p) {
+    p->addStateResidencyDataProvider(std::make_unique<FakeStateResidencyDataProvider>(
+            "Display", std::vector<State>{{0, "Off"}, {1, "On"}}));
+}
+
+void addFakeEnergyConsumer1(std::shared_ptr<PowerStats> p) {
+    p->addEnergyConsumer(std::make_unique<FakeEnergyConsumer>(EnergyConsumerType::OTHER, "GPU"));
+}
+
+void addFakeEnergyConsumer2(std::shared_ptr<PowerStats> p) {
+    p->addEnergyConsumer(
+            std::make_unique<FakeEnergyConsumer>(EnergyConsumerType::MOBILE_RADIO, "MODEM"));
+}
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<PowerStats> p = ndk::SharedRefBase::make<PowerStats>();
+
+    setFakeEnergyMeter(p);
+
+    addFakeStateResidencyDataProvider1(p);
+    addFakeStateResidencyDataProvider2(p);
+
+    addFakeEnergyConsumer1(p);
+    addFakeEnergyConsumer2(p);
+
+    const std::string instance = std::string() + PowerStats::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(p->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/power/stats/aidl/default/power.stats-default.rc b/power/stats/aidl/default/power.stats-default.rc
new file mode 100644
index 0000000..6ff6754
--- /dev/null
+++ b/power/stats/aidl/default/power.stats-default.rc
@@ -0,0 +1,4 @@
+service vendor.power.stats-default /vendor/bin/hw/android.hardware.power.stats-service.example
+    class hal
+    user system
+    group system
diff --git a/power/stats/aidl/default/power.stats-default.xml b/power/stats/aidl/default/power.stats-default.xml
new file mode 100644
index 0000000..3b1a216
--- /dev/null
+++ b/power/stats/aidl/default/power.stats-default.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.power.stats</name>
+        <fqname>IPowerStats/default</fqname>
+    </hal>
+</manifest>
diff --git a/power/stats/aidl/vts/Android.bp b/power/stats/aidl/vts/Android.bp
new file mode 100644
index 0000000..b556548
--- /dev/null
+++ b/power/stats/aidl/vts/Android.bp
@@ -0,0 +1,41 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalPowerStatsTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalPowerStatsTargetTest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+    ],
+    static_libs: [
+        "android.hardware.power.stats-V1-ndk_platform",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
new file mode 100644
index 0000000..c7ba96c
--- /dev/null
+++ b/power/stats/aidl/vts/VtsHalPowerStatsTargetTest.cpp
@@ -0,0 +1,439 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/power/stats/IPowerStats.h>
+#include <android-base/properties.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <algorithm>
+#include <iterator>
+#include <random>
+#include <unordered_map>
+
+using aidl::android::hardware::power::stats::Channel;
+using aidl::android::hardware::power::stats::EnergyConsumer;
+using aidl::android::hardware::power::stats::EnergyConsumerAttribution;
+using aidl::android::hardware::power::stats::EnergyConsumerResult;
+using aidl::android::hardware::power::stats::EnergyConsumerType;
+using aidl::android::hardware::power::stats::EnergyMeasurement;
+using aidl::android::hardware::power::stats::IPowerStats;
+using aidl::android::hardware::power::stats::PowerEntity;
+using aidl::android::hardware::power::stats::State;
+using aidl::android::hardware::power::stats::StateResidency;
+using aidl::android::hardware::power::stats::StateResidencyResult;
+
+using ndk::SpAIBinder;
+
+#define ASSERT_OK(a)                                     \
+    do {                                                 \
+        auto ret = a;                                    \
+        ASSERT_TRUE(ret.isOk()) << ret.getDescription(); \
+    } while (0)
+
+class PowerStatsAidl : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        powerstats = IPowerStats::fromBinder(
+                SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(nullptr, powerstats.get());
+    }
+
+    template <typename T>
+    std::vector<T> getRandomSubset(std::vector<T> const& collection);
+
+    void testNameValid(const std::string& name);
+
+    template <typename T, typename S>
+    void testUnique(std::vector<T> const& collection, S T::*field);
+
+    template <typename T, typename S, typename R>
+    void testMatching(std::vector<T> const& c1, R T::*f1, std::vector<S> const& c2, R S::*f2);
+
+    std::shared_ptr<IPowerStats> powerstats;
+};
+
+// Returns a random subset from a collection
+template <typename T>
+std::vector<T> PowerStatsAidl::getRandomSubset(std::vector<T> const& collection) {
+    if (collection.empty()) {
+        return {};
+    }
+
+    std::vector<T> selected;
+    std::sample(collection.begin(), collection.end(), std::back_inserter(selected),
+                rand() % collection.size() + 1, std::mt19937{std::random_device{}()});
+
+    return selected;
+}
+
+// Tests whether a name is valid
+void PowerStatsAidl::testNameValid(const std::string& name) {
+    EXPECT_NE(name, "");
+}
+
+// Tests whether the fields in a given collection are unique
+template <typename T, typename S>
+void PowerStatsAidl::testUnique(std::vector<T> const& collection, S T::*field) {
+    std::set<S> cSet;
+    for (auto const& elem : collection) {
+        EXPECT_TRUE(cSet.insert(elem.*field).second);
+    }
+}
+
+template <typename T, typename S, typename R>
+void PowerStatsAidl::testMatching(std::vector<T> const& c1, R T::*f1, std::vector<S> const& c2,
+                                  R S::*f2) {
+    std::set<R> c1fields, c2fields;
+    for (auto elem : c1) {
+        c1fields.insert(elem.*f1);
+    }
+
+    for (auto elem : c2) {
+        c2fields.insert(elem.*f2);
+    }
+
+    EXPECT_EQ(c1fields, c2fields);
+}
+
+// Each PowerEntity must have a valid name
+TEST_P(PowerStatsAidl, ValidatePowerEntityNames) {
+    std::vector<PowerEntity> infos;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&infos));
+
+    for (auto info : infos) {
+        testNameValid(info.name);
+    }
+}
+
+// Each power entity must have a unique name
+TEST_P(PowerStatsAidl, ValidatePowerEntityUniqueNames) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    testUnique(entities, &PowerEntity::name);
+}
+
+// Each PowerEntity must have a unique ID
+TEST_P(PowerStatsAidl, ValidatePowerEntityIds) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    testUnique(entities, &PowerEntity::id);
+}
+
+// Each power entity must have at least one state
+TEST_P(PowerStatsAidl, ValidateStateSize) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    for (auto entity : entities) {
+        EXPECT_GT(entity.states.size(), 0);
+    }
+}
+
+// Each state must have a valid name
+TEST_P(PowerStatsAidl, ValidateStateNames) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    for (auto entity : entities) {
+        for (auto state : entity.states) {
+            testNameValid(state.name);
+        }
+    }
+}
+
+// Each state must have a name that is unique to the given PowerEntity
+TEST_P(PowerStatsAidl, ValidateStateUniqueNames) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    for (auto entity : entities) {
+        testUnique(entity.states, &State::name);
+    }
+}
+
+// Each state must have an ID that is unique to the given PowerEntity
+TEST_P(PowerStatsAidl, ValidateStateUniqueIds) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    for (auto entity : entities) {
+        testUnique(entity.states, &State::id);
+    }
+}
+
+// State residency must return a valid status
+TEST_P(PowerStatsAidl, TestGetStateResidency) {
+    std::vector<StateResidencyResult> results;
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
+}
+
+// State residency must return all results
+TEST_P(PowerStatsAidl, TestGetStateResidencyAllResults) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    std::vector<StateResidencyResult> results;
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
+
+    testMatching(entities, &PowerEntity::id, results, &StateResidencyResult::id);
+}
+
+// Each result must contain all state residencies
+TEST_P(PowerStatsAidl, TestGetStateResidencyAllStateResidencies) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+
+    std::vector<StateResidencyResult> results;
+    ASSERT_OK(powerstats->getStateResidency({}, &results));
+
+    for (auto entity : entities) {
+        auto it = std::find_if(results.begin(), results.end(),
+                               [&entity](const auto& x) { return x.id == entity.id; });
+        ASSERT_NE(it, results.end());
+
+        testMatching(entity.states, &State::id, it->stateResidencyData, &StateResidency::id);
+    }
+}
+
+// State residency must return results for each requested power entity
+TEST_P(PowerStatsAidl, TestGetStateResidencySelectedResults) {
+    std::vector<PowerEntity> entities;
+    ASSERT_OK(powerstats->getPowerEntityInfo(&entities));
+    if (entities.empty()) {
+        return;
+    }
+
+    std::vector<PowerEntity> selectedEntities = getRandomSubset(entities);
+    std::vector<int32_t> selectedIds;
+    for (auto const& entity : selectedEntities) {
+        selectedIds.push_back(entity.id);
+    }
+
+    std::vector<StateResidencyResult> selectedResults;
+    ASSERT_OK(powerstats->getStateResidency(selectedIds, &selectedResults));
+
+    testMatching(selectedEntities, &PowerEntity::id, selectedResults, &StateResidencyResult::id);
+}
+
+// Energy meter info must return a valid status
+TEST_P(PowerStatsAidl, TestGetEnergyMeterInfo) {
+    std::vector<Channel> info;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&info));
+}
+
+// Each channel must have a valid name
+TEST_P(PowerStatsAidl, ValidateChannelNames) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    for (auto channel : channels) {
+        testNameValid(channel.name);
+    }
+}
+
+// Each channel must have a valid subsystem
+TEST_P(PowerStatsAidl, ValidateSubsystemNames) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    for (auto channel : channels) {
+        testNameValid(channel.subsystem);
+    }
+}
+
+// Each channel must have a unique name
+TEST_P(PowerStatsAidl, ValidateChannelUniqueNames) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    testUnique(channels, &Channel::name);
+}
+
+// Each channel must have a unique ID
+TEST_P(PowerStatsAidl, ValidateChannelUniqueIds) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    testUnique(channels, &Channel::id);
+}
+
+// Reading energy meter must return a valid status
+TEST_P(PowerStatsAidl, TestReadEnergyMeter) {
+    std::vector<EnergyMeasurement> data;
+    ASSERT_OK(powerstats->readEnergyMeter({}, &data));
+}
+
+// Reading energy meter must return results for all available channels
+TEST_P(PowerStatsAidl, TestGetAllEnergyMeasurements) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+
+    std::vector<EnergyMeasurement> measurements;
+    ASSERT_OK(powerstats->readEnergyMeter({}, &measurements));
+
+    testMatching(channels, &Channel::id, measurements, &EnergyMeasurement::id);
+}
+
+// Reading energy must must return results for each selected channel
+TEST_P(PowerStatsAidl, TestGetSelectedEnergyMeasurements) {
+    std::vector<Channel> channels;
+    ASSERT_OK(powerstats->getEnergyMeterInfo(&channels));
+    if (channels.empty()) {
+        return;
+    }
+
+    std::vector<Channel> selectedChannels = getRandomSubset(channels);
+    std::vector<int32_t> selectedIds;
+    for (auto const& channel : selectedChannels) {
+        selectedIds.push_back(channel.id);
+    }
+
+    std::vector<EnergyMeasurement> selectedMeasurements;
+    ASSERT_OK(powerstats->readEnergyMeter(selectedIds, &selectedMeasurements));
+
+    testMatching(selectedChannels, &Channel::id, selectedMeasurements, &EnergyMeasurement::id);
+}
+
+// Energy consumer info must return a valid status
+TEST_P(PowerStatsAidl, TestGetEnergyConsumerInfo) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+}
+
+// Each energy consumer must have a unique id
+TEST_P(PowerStatsAidl, TestGetEnergyConsumerUniqueId) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+
+    testUnique(consumers, &EnergyConsumer::id);
+}
+
+// Each energy consumer must have a valid name
+TEST_P(PowerStatsAidl, ValidateEnergyConsumerNames) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+
+    for (auto consumer : consumers) {
+        testNameValid(consumer.name);
+    }
+}
+
+// Each energy consumer must have a unique name
+TEST_P(PowerStatsAidl, ValidateEnergyConsumerUniqueNames) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+
+    testUnique(consumers, &EnergyConsumer::name);
+}
+
+// Energy consumers of the same type must have ordinals that are 0,1,2,..., N - 1
+TEST_P(PowerStatsAidl, ValidateEnergyConsumerOrdinals) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+
+    std::unordered_map<EnergyConsumerType, std::set<int32_t>> ordinalMap;
+
+    // Ordinals must be unique for each type
+    for (auto consumer : consumers) {
+        EXPECT_TRUE(ordinalMap[consumer.type].insert(consumer.ordinal).second);
+    }
+
+    // Min ordinal must be 0, max ordinal must be N - 1
+    for (const auto& [unused, ordinals] : ordinalMap) {
+        EXPECT_EQ(0, *std::min_element(ordinals.begin(), ordinals.end()));
+        EXPECT_EQ(ordinals.size() - 1, *std::max_element(ordinals.begin(), ordinals.end()));
+    }
+}
+
+// Energy consumed must return a valid status
+TEST_P(PowerStatsAidl, TestGetEnergyConsumed) {
+    std::vector<EnergyConsumerResult> results;
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
+}
+
+// Energy consumed must return data for all energy consumers
+TEST_P(PowerStatsAidl, TestGetAllEnergyConsumed) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+
+    std::vector<EnergyConsumerResult> results;
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
+
+    testMatching(consumers, &EnergyConsumer::id, results, &EnergyConsumerResult::id);
+}
+
+// Energy consumed must return data for each selected energy consumer
+TEST_P(PowerStatsAidl, TestGetSelectedEnergyConsumed) {
+    std::vector<EnergyConsumer> consumers;
+    ASSERT_OK(powerstats->getEnergyConsumerInfo(&consumers));
+    if (consumers.empty()) {
+        return;
+    }
+
+    std::vector<EnergyConsumer> selectedConsumers = getRandomSubset(consumers);
+    std::vector<int32_t> selectedIds;
+    for (auto const& consumer : selectedConsumers) {
+        selectedIds.push_back(consumer.id);
+    }
+
+    std::vector<EnergyConsumerResult> selectedResults;
+    ASSERT_OK(powerstats->getEnergyConsumed(selectedIds, &selectedResults));
+
+    testMatching(selectedConsumers, &EnergyConsumer::id, selectedResults,
+                 &EnergyConsumerResult::id);
+}
+
+// Energy consumed attribution uids must be unique for a given energy consumer
+TEST_P(PowerStatsAidl, ValidateEnergyConsumerAttributionUniqueUids) {
+    std::vector<EnergyConsumerResult> results;
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
+
+    for (auto result : results) {
+        testUnique(result.attribution, &EnergyConsumerAttribution::uid);
+    }
+}
+
+// Energy consumed total energy >= sum total of uid-attributed energy
+TEST_P(PowerStatsAidl, TestGetEnergyConsumedAttributedEnergy) {
+    std::vector<EnergyConsumerResult> results;
+    ASSERT_OK(powerstats->getEnergyConsumed({}, &results));
+
+    for (auto result : results) {
+        int64_t totalAttributedEnergyUWs = 0;
+        for (auto attribution : result.attribution) {
+            totalAttributedEnergyUWs += attribution.energyUWs;
+        }
+        EXPECT_TRUE(result.energyUWs >= totalAttributedEnergyUWs);
+    }
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(PowerStatsAidl);
+INSTANTIATE_TEST_SUITE_P(
+        PowerStats, PowerStatsAidl,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IPowerStats::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/prebuilt_hashes/26.txt b/prebuilt_hashes/26.txt
deleted file mode 100644
index f2feb4c..0000000
--- a/prebuilt_hashes/26.txt
+++ /dev/null
@@ -1,186 +0,0 @@
-# Do not change this file except to add new interfaces. Changing
-# pre-existing interfaces will fail VTS and break framework-only OTAs
-
-# HALs released in Android O
-
-f219c3b5b8c6cb1d659d4c7328f67246abfe1a8613f469826fd3b9ad090417a2 android.hardware.audio@2.0::IDevice
-4d579cae1cd87a783fd49233e10ce720ba183cfd1d5ccd80149e69de5c1c7362 android.hardware.audio@2.0::IDevicesFactory
-203e23f18011390b8cd10c303e0c16c4eebc8fa187e80e40d6be4624c2b0848a android.hardware.audio@2.0::IPrimaryDevice
-aaf93123deec336eb247ad8099849469a541ca0cf7c28c5f5336cebe1ee86748 android.hardware.audio@2.0::IStream
-0468c5723b0d44c5b451bdfa06153000c6f352fd3336e0ad2697127b04b766df android.hardware.audio@2.0::IStreamIn
-7296f7064fd3ab24082b43a1da34cc876268065310b785499fba5178d063680a android.hardware.audio@2.0::IStreamOut
-19d241d71c3e1140afba8140dcb57448446025a5fc03739788c4c25e9a98b6c2 android.hardware.audio@2.0::IStreamOutCallback
-c84da9f586087227daa3b96d42b4575326bccfd5bc8a2a5acf86d774f8ea2648 android.hardware.audio@2.0::types
-1305786c06e22b9b24ebde136054cc827b63c86d8bf4a136d6f7f54752b8566b android.hardware.audio.common@2.0::types
-fa8fbae3d1da3c264e4f3110728076abc09b4e65f12af6ae136367328de988ab android.hardware.audio.effect@2.0::IAcousticEchoCancelerEffect
-ca4752545d54547ff069eae161af7550cb5f5a7e8b60316ddd132a30906a68e7 android.hardware.audio.effect@2.0::IAutomaticGainControlEffect
-d2b8af988dc66f514d886bcee44b440d8034bc2a762f7161717ef3c956073067 android.hardware.audio.effect@2.0::IBassBoostEffect
-611bc09c75e796f3512b1ca6be508b0a9ba996759b8a2c60507784ff58076229 android.hardware.audio.effect@2.0::IDownmixEffect
-36a57369dfdc75180e8b64ae80b1970db8f6d9085dbff6ca931715038cc056e1 android.hardware.audio.effect@2.0::IEffect
-d2aa2df6d189c580f5be8460fa0ff4134d9c05a383f3204659baee426a6f0edf android.hardware.audio.effect@2.0::IEffectBufferProviderCallback
-217f9161983a48d3bf3faeb158f868aa8bf0ce25889e4ee3d2bab1a2e8d33e77 android.hardware.audio.effect@2.0::IEffectsFactory
-c2b38bc07991e880c83ca8cb88181411eeef708b8b936aedd2f2e0acade7df69 android.hardware.audio.effect@2.0::IEnvironmentalReverbEffect
-2ff9f9704be5f167745b4de790e9dafc3cc4719e2f6e2e5497085e679853cfe7 android.hardware.audio.effect@2.0::IEqualizerEffect
-c31447fb02dbc8b56c359941dad22f416511860173c5c5fd278d1bf2312b13de android.hardware.audio.effect@2.0::ILoudnessEnhancerEffect
-804831ca258802eb3eb65a0a7b5d5e3d37d4a15ba8c2836b4276eda98b47e1d0 android.hardware.audio.effect@2.0::INoiseSuppressionEffect
-778fd5b9837f481d8e47425b3e2a3bd0c6362a0b6870291518e2d863530fdb61 android.hardware.audio.effect@2.0::IPresetReverbEffect
-c93cb25a1a92d07aa80a617c01e8d22fc97bf8cefd3962b6a5be386ad4704d89 android.hardware.audio.effect@2.0::IVirtualizerEffect
-918f331780c9c7b04f2151a2e563aab088198ede8e6f865302ebaa13905bd9ce android.hardware.audio.effect@2.0::IVisualizerEffect
-4caad099f8fc00262b6c03ba41271808b37cea90ac98b534299bbf4ee823af02 android.hardware.audio.effect@2.0::types
-# android.hardware.automotive.* are unfrozen
-1fbdc1f852f8bd2e4a6c5cb30ac2b78668c98dce118a61762d4034ae859f43d8 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint
-aabb5c3c585592d71ee57b77298c14993d77914ddeaa64b2c5109a602b02ea47 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprintClientCallback
-1ec60d4efddae9a7b2469278a576967b4751e88de5b8d7e9df6eff6bc0da7bc9 android.hardware.biometrics.fingerprint@2.1::types
-347ce746815607567f5f3b53e4800998ca5ab9355141f0880fc0cf0c1fc5c355 android.hardware.bluetooth@1.0::IBluetoothHci
-835f41be2281bfb22f3e33c6fa870bde7bc21e37e5cfbaf9a36fff170632f754 android.hardware.bluetooth@1.0::IBluetoothHciCallbacks
-a8dfd0dbe463a3cdbcf1d985b38a28b3d93ba2ae5a1d1db4aaef4c38a5781b91 android.hardware.bluetooth@1.0::types
-7192d756aeba00aba32f4504981df8172ffca83e210c4838dabf295e53e93590 android.hardware.boot@1.0::IBootControl
-cebaa803b8e33807a0d69f46652b650ccb549e8f9b19d6becbbf26690e828b49 android.hardware.boot@1.0::types
-a98d49f23712a7cc327d1e0602d05f6f3ad32cfb5c74711d009c726611ee1c93 android.hardware.broadcastradio@1.0::IBroadcastRadio
-ed82579c0c165feaa12d0e33c06b3342ab41ec0a439247f202775e8369e46ef6 android.hardware.broadcastradio@1.0::IBroadcastRadioFactory
-da6ab32ee2793d2c86d3b603075d5383852b89d7eaa201861aa0473d418f3c7f android.hardware.broadcastradio@1.0::ITuner
-04d3ca022e25c308d9efd2e7eb77b3a7a206907cdc1b9ea9326340b377868172 android.hardware.broadcastradio@1.0::ITunerCallback
-bd42c8d7838cfed1998b49c39745dec116d2d6edc2c11a4c0399b8f3a1d1655a android.hardware.broadcastradio@1.0::types
-81164323115d6588e259e8319fddf7487adfa1f49ce60f7e80ba74e0783392a4 android.hardware.camera.common@1.0::types
-c1705e9d62438a1d955269965af915ae28e692bd480a3b1ce67056fef992d62f android.hardware.camera.device@1.0::ICameraDevice
-78e9b44cf8660bdc1e98dca07451804153824efcd28db208a62f5ad728f44076 android.hardware.camera.device@1.0::ICameraDeviceCallback
-28f0386ba86ddf41e53a8117b48a0328d7a4d2574213e89f4a1062398a244566 android.hardware.camera.device@1.0::ICameraDevicePreviewCallback
-4db48439ce9dde97f1cfb3d7408f6c737f621ac0f7494aeea35ed599bc2352a3 android.hardware.camera.device@1.0::types
-b32f9aeaf1c442195eb06ffc7600968c919d005b2718874f09c57287fae55918 android.hardware.camera.device@3.2::ICameraDevice
-63bfc3da0f2d2301f7a0508c7c2b9ffc521d4d545ee03718da70e9d6273b3b21 android.hardware.camera.device@3.2::ICameraDeviceCallback
-0fa3e1e64819283b8737fc4e5ab759f0cb4ac1a996e8a51cc4aa8025a457208e android.hardware.camera.device@3.2::ICameraDeviceSession
-030be3d2b159cbde7920485807140f6b6064ef4a5de4a40a6c4bc8d2c72f7cd3 android.hardware.camera.device@3.2::types
-5ba7947cee515d7a2359bfcbfb9678c1c3a768c288471919ac095b96ae6f3d40 android.hardware.camera.metadata@3.2::types
-f7e299d85033ac52d1095a35784fcfeaff43603f58c751e4153c85bbade3b330 android.hardware.camera.provider@2.4::ICameraProvider
-a501ca1aecd09f1b9fd70a9af84205430dbd49a808e8fa395d363b9902e6f58c android.hardware.camera.provider@2.4::ICameraProviderCallback
-7f5fe8f4f8a24037153c504d8b4d3313c2ce33d81c8c69fe5194ddd2d4080e72 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
-87beacc481897cf02fb1628d75e68133de6d74d4cffe582cda2f5e16bdd74516 android.hardware.configstore@1.0::types
-a5ae0fe8667f0b1af09b13e72d29600f4eb3853b257079c45a99b6f4a3360649 android.hardware.contexthub@1.0::IContexthub
-2ab3054c2d9302d8417ee7495353a2887fe338f913276f2eb41e80f11395ec2e android.hardware.contexthub@1.0::IContexthubCallback
-c3b2b37d561d31ea094411f251bf73bea334f4fe849a4390aef5e20bca6cadba android.hardware.contexthub@1.0::types
-df174c1871c864b4c79ca9f64aae7936d24a272eca3191a30458ca2b706dec79 android.hardware.drm@1.0::ICryptoFactory
-83639e90caeb996b0274e420de3cd556779de1ca48464b68eee799bef32b34cd android.hardware.drm@1.0::ICryptoPlugin
-1440cffdfaeb12830ac10ee6ffdb0f1083e701057b806df11fb4787b4c91e718 android.hardware.drm@1.0::IDrmFactory
-78ba33b108f620e6a0eec01ef654547e69a85754578ea4c9ef03ec205f16121c android.hardware.drm@1.0::IDrmPlugin
-701d9e51952172364e4ea70db9c397f08c3b4577ba33051f050a6cdd532de1b4 android.hardware.drm@1.0::IDrmPluginListener
-4238d62ad90df63aa338c6f1b6264c09c5a3706945d5c49d1189c0be1dc9e942 android.hardware.drm@1.0::types
-f07b1ee3ba11a2fc9f200421b2e1afb7c1854ee987000e45c987fb9507795055 android.hardware.dumpstate@1.0::IDumpstateDevice
-c9d318df7922bde3b265927b521ff5a965002826fc0cabfcaef52a56760f2d34 android.hardware.gatekeeper@1.0::IGatekeeper
-da13bd69282fb275767abb18704c57ff8038e6c139ad17157dc702810f70d06a android.hardware.gatekeeper@1.0::types
-37c7da4f823ec958dfa9c960e2d341c48f877e0bfa758f3fa9e2d9c1e1bd66d9 android.hardware.gnss@1.0::IAGnss
-7ec9afdb964bfb8369866913caf018f2636592885bcb558a65de2c5436ab4f60 android.hardware.gnss@1.0::IAGnssCallback
-d16e6a359be6963ea753d7138e84ecf2b93052097938938c4d36d7a47ea2e2ae android.hardware.gnss@1.0::IAGnssRil
-2f907708d74d94b1e121ed27651c9c72af65952d347b58ff07dac5d5d7a7f678 android.hardware.gnss@1.0::IAGnssRilCallback
-5ac7edad06d76064b882be161f3f9d9692a997ec72e9f36addb7fe8918f49992 android.hardware.gnss@1.0::IGnss
-b05c983c87c3376e145223688c3b541b5e11b827f211e38d5a31af1ca3a2e222 android.hardware.gnss@1.0::IGnssBatching
-4981d2d2c4e725c7544be0956099a91fc7bbc8048c563394158083fe924e651e android.hardware.gnss@1.0::IGnssBatchingCallback
-3cd22d92cc1f935150c5048310e84886f14eed2556e8f00636733d204045cc4f android.hardware.gnss@1.0::IGnssCallback
-175185a5eda87476193ca5461df75dd16d36664591e8130530dd8ef0eb2ddf6a android.hardware.gnss@1.0::IGnssConfiguration
-4542122b96fbf27101cb8222bafb76e7c8d032d977dd1058edd8e5881ca5752f android.hardware.gnss@1.0::IGnssDebug
-e6dd0c8416e523ab9cbd14d56ab6f016481a8aef3bc8a750051122d31075f6c7 android.hardware.gnss@1.0::IGnssGeofenceCallback
-f90e4ddc652706299d8e3d8ba18e0745c3bae9bf4d1be6bd06d9c1f50ec8d28a android.hardware.gnss@1.0::IGnssGeofencing
-9ea8987bb1089c8c5d7b67866575b866ef516045021d9efcc37c6352bce072a3 android.hardware.gnss@1.0::IGnssMeasurement
-cf20492673d6a423e4c2e87fdfb5a4c4a602431721978db852e246f258e25edb android.hardware.gnss@1.0::IGnssMeasurementCallback
-af85aa0f48ae99a39f4688c344e4419304f681f9af818a5c8d759286fc4418de android.hardware.gnss@1.0::IGnssNavigationMessage
-76b0874ea4c06b29f66418c59820f4286b3be9629cd872923d0dfbb602cd432d android.hardware.gnss@1.0::IGnssNavigationMessageCallback
-248bcf51da4273d64f367bf6877baef2feeaca365459842fd3c214a2dc6e0224 android.hardware.gnss@1.0::IGnssNi
-c781b7b125f68be5db8a8c3d412d526acdbdf77dcc592a4c0ed70b8ce4fe6c49 android.hardware.gnss@1.0::IGnssNiCallback
-c1142657de16fdb292a502372fe938614d65270ab8359217d6e13604fe4dbca4 android.hardware.gnss@1.0::IGnssXtra
-bd366b83d8d565d0e8bfabff3adfcab0259d75b4e2a9f8e1b91e11d1593a2ffb android.hardware.gnss@1.0::IGnssXtraCallback
-881bc2f94026784d194cffbff166c6e8bf911de4e02abe96fc7d89ec75b0574a android.hardware.gnss@1.0::types
-17971eb8a482893dadcfc16e0583f492d42a034ef95d9b0b709417af30838396 android.hardware.graphics.allocator@2.0::IAllocator
-60bf42a4898e4fb70dbd720b263aeafd7f35f5e1a5effeabb4d5d659878a5f18 android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer
-b8a75617b9ec12bea641f3a73d4025a33e8b9a2f9169dd46094af56adf9249c5 android.hardware.graphics.bufferqueue@1.0::IProducerListener
-4f6dedbcdd21c309dfc650acea81a096d6b242493ffe49c8d61bd3c43aad354e android.hardware.graphics.common@1.0::types
-b3aac6c3817f039964fcd62268274b3039e17bd7d0d5b40b4d1d1c7b19a1f866 android.hardware.graphics.composer@2.1::IComposer
-b19d00eb8a8b3b0034a0321f22e8f32162bf4c2aebbce6da22c025f56e459ea2 android.hardware.graphics.composer@2.1::IComposerCallback
-61ee43ffe6fb6dbe8b22dc17c51ff3d5ba703fc6029cba211f901f3d79c8a72d android.hardware.graphics.composer@2.1::IComposerClient
-1c98c2f5154345312ec054871792a2982ec5f3e2bc2abfb61a10c0b517978e20 android.hardware.graphics.composer@2.1::types
-a695898589e1ef15b2b2510f11edd6aafac9918d9cf8d74b4b6143b309dee542 android.hardware.graphics.mapper@2.0::IMapper
-28507d385a3dd224bf3c32f1bfd9f96092c4701b9c1cc66caa578fc3efc97877 android.hardware.graphics.mapper@2.0::types
-91e2ba3805c923f01fc1231ec9ff838942aee3346f2d7614ecc0caeadbe57ed4 android.hardware.health@1.0::IHealth
-1275aa2e8732909101b26aec49ed2285489e89d97b8610a8908b7868e35a3cc5 android.hardware.health@1.0::types
-3a8d3922e06e6d4f8e0befc6be78d0e9e07aed1585b3da6521bed406d25a9483 android.hardware.ir@1.0::IConsumerIr
-7090bd37912fcf723a12f4bc17783e3527577c4944805a028c296fd7a95bd682 android.hardware.ir@1.0::types
-cc7925a78c0ab022515f48840d3dae76f384ed3a1287abadcb461a5cd5396163 android.hardware.keymaster@3.0::IKeymasterDevice
-822998d7bb76f0cd719a409291434fcb56e6d50bc4780788bb157a3374d63b8c android.hardware.keymaster@3.0::types
-d4ed2f0e14f9e914d0b1275d2e0363192fe30aca9059c84edb5fad15995f9ec4 android.hardware.light@2.0::ILight
-d9584bfcaedd6e62cf337881748246b23e36cbc2bc3aa84c01b6a1e622061400 android.hardware.light@2.0::types
-16c0cf0f73de1e5208a95020c6c6474903e7094f76b2d782651afaca0e5fd86f android.hardware.media@1.0::types
-8bc2f5fdcad68856eb61a62fe4cc043fa064bb7f1dab95a71d1918ec1eef7b55 android.hardware.media.omx@1.0::IGraphicBufferSource
-0d3de9cd89d4718ea3b772f2d8b93be004feb3abb7e7dc5402e37047cc730d05 android.hardware.media.omx@1.0::IOmx
-32002e1c358c64de106c977a6dc6af7da27be4803a5bb66fd6f891a5ba0a1617 android.hardware.media.omx@1.0::IOmxBufferSource
-81ad8d8bb1cf6f41923cf11dd39354a8fe433db284a234cc675de7e75a82224c android.hardware.media.omx@1.0::IOmxNode
-494c0c8bf6065edc82ec127228ed19dd2243dc1c2f7d601c7c6be7b7015c1713 android.hardware.media.omx@1.0::IOmxObserver
-252c2fc50c78fd6de8365e5b60e5115119ace107db0b94b0b26815cbf3d2b64a android.hardware.media.omx@1.0::IOmxStore
-148c1b50b0958988373145ffdf5fa0e1b6534e0a2034a570e74b15c127cf7c5e android.hardware.media.omx@1.0::types
-c66902fe48d687ac6740a3e32ae55fb75532c48c36c6386461c2b4416ad2e0f1 android.hardware.memtrack@1.0::IMemtrack
-860bacd8b11a269c40567542b613fe4ca448d5cb4326d0058899e608e89dfca1 android.hardware.memtrack@1.0::types
-07ac2dc95270321ec7d4c33cd25e5085a057f47fe350d645af6f7a7a11e3cf57 android.hardware.nfc@1.0::INfc
-f2fe54426c07d67388d4774a60641ad4c0538f22eb6e1111722f231772655de6 android.hardware.nfc@1.0::INfcClientCallback
-9626fd18db113d709faf593a70caf19bd0980294d23c468c80c30186f9d298a6 android.hardware.nfc@1.0::types
-deee1dc4948f33af207e1008aba0f6cc07afb7900eab53f33192c8cac137eefc android.hardware.power@1.0::IPower
-efc83df3f962b93c7c0290d691d7d300dabe12683e2cde3591fb3c0beedce20f android.hardware.power@1.0::types
-9b5aa499ec3b4226f15f48f5ed08896e2fc0676f978c9e199c1da21daaf002a6 android.hardware.radio@1.0::IRadio
-5c8efbb9c451a59737ed2c6c20230aae4745839ca01d8088d6dcc9020e52d2c5 android.hardware.radio@1.0::IRadioIndication
-69f6b4b8ec40ca02ccc7bb8227a097135c20c00bd94c822e421cd9af1267252c android.hardware.radio@1.0::IRadioResponse
-de3ab9f73b1073cd677b19d886fb927e9381b30161a704712d2b30f875873f5c android.hardware.radio@1.0::ISap
-d183e406ef0897df2117a9dde384e8e6ea4fa6ab1c7f6f28e65b87011218c9ea android.hardware.radio@1.0::ISapCallback
-96986fbd22f0e6ca752e1fcdc0a64bda213995a81f5f36bc4faf3532d9306b97 android.hardware.radio@1.0::types
-00f70085d6fae1d482fb700a3fd42ed475384c95b51c9269b9ae5037b74ad4dd android.hardware.radio.deprecated@1.0::IOemHook
-06837b6d7e843cfa9cd20fed4070feca7a9b5c81a9ed643bf7d06803455a9816 android.hardware.radio.deprecated@1.0::IOemHookIndication
-6fd4874f0eddd4626a27658fd94fad526c317f3563439e79718bdb1a3a2309d5 android.hardware.radio.deprecated@1.0::IOemHookResponse
-6983a2cafe39d5c57dfdc1743055fb0f757a0df8c78e00423d5e1810836927e1 android.hardware.renderscript@1.0::IContext
-7f9417a0ccf78ea042ec7a8ac8e3750346d4d9d7e5ae01b1b35fde303f47c24d android.hardware.renderscript@1.0::IDevice
-fc6f325b266b32353f7d1534fbe58e0d368265a12b77fa396fb556e8c443f739 android.hardware.renderscript@1.0::types
-89585ff541c319de4091a5a0b687dd526ac81c6382ffd7b979a4164b3d7419a6 android.hardware.sensors@1.0::ISensors
-e04ab978fc28f4c515f4a75617dfda8607733a64f13666beeb0e604a07a39333 android.hardware.sensors@1.0::types
-5befc019cbe94953661e2cdb95e3cf64f5e565c29403e1c2daecc2be44e0a55c android.hardware.soundtrigger@2.0::ISoundTriggerHw
-d7ec5f612a5e0a59ea4f2b61317e208ff56dd50920fd4eb441e0cbc8f97e4f49 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
-5bee9e70f7e5ec7ee252883b28f98f12b59960f4c2a0b4cc9a4526e4669ebcd4 android.hardware.soundtrigger@2.0::types
-97f1ec446043bc5a6645b74529a6276496bdb35e0aee41eda55cb92d51eb7802 android.hardware.thermal@1.0::IThermal
-84965a6908bceb4ef51546bf8731f309f1ea9d09a0177dcc7974132e523dd6d2 android.hardware.thermal@1.0::types
-938850621c3c5ef426a4b88e752ba99b3559037e782a3d938604f3aef5cc0f1b android.hardware.tv.cec@1.0::IHdmiCec
-e75b6eea711d36fac678bce072b3cec6544b27fa9f4cd903999404e5c110ca10 android.hardware.tv.cec@1.0::IHdmiCecCallback
-6e25f8dbfadb668e1d4ec80eb9acd95d8bc9e0a240c36d27e662adb440314b95 android.hardware.tv.cec@1.0::types
-0cafa3c8388e9631916d2d800f78decbcec2904f11415b32c71a31d9a51ebf79 android.hardware.tv.input@1.0::ITvInput
-dee83e5c864fbbee8f02448d0800be32f06856386b9f907bc5d952208c9434f9 android.hardware.tv.input@1.0::ITvInputCallback
-07aab30410b612381234dca6d453d4ab96f2e536368715717c6e28101b9851b5 android.hardware.tv.input@1.0::types
-4ef57499273f38bdbdd0c15e56ee7a4bc5f18a5644092170a531df3541d9e015 android.hardware.usb@1.0::IUsb
-4be7881e411ba42784bf5b7354c14ae0cf161004d39433aaecaab0d19ea99354 android.hardware.usb@1.0::IUsbCallback
-f7e6e747910a3cd0a35846141e3b990a6a612a297b2b70ccd5740b646a450a8c android.hardware.usb@1.0::types
-06ea64cc3565777f3b259e400ffa7100d07f3827ad9357b0c5d3c651384e5553 android.hardware.vibrator@1.0::IVibrator
-0fecd34ae64f32eff6aa615fd662349242c0b8b6e303ef05a7cb5776c732f413 android.hardware.vibrator@1.0::types
-4b962968a7df4ab104d1315d66a0a7348a713fecbb5d2c1b23688494458f37ce android.hardware.vr@1.0::IVr
-b9be36719a8ad534000a51ea07be91be94c405bf1e038ae825acf65087ffd378 android.hardware.wifi@1.0::IWifi
-ee0224ee18813506d9d6f13d8c8e4679f053c290a443a52a7c52a5d3c852262b android.hardware.wifi@1.0::IWifiApIface
-f3eecc489deb4c74892f59eb7adb769063bd5c354ac132b626a5f42b363d36bc android.hardware.wifi@1.0::IWifiChip
-a1b988377645a58e5e2542ca2bad4e17c21a4a389213d05de2f0e32d57b7d339 android.hardware.wifi@1.0::IWifiChipEventCallback
-5ed6760ce77e84bc6c49d1acb3f7d8117c9176b3f06514bc44ad3af84c80dcfe android.hardware.wifi@1.0::IWifiEventCallback
-6b9ad43a5efbe6ca214f751e22ce43cf5cd4d5d5f2cba80f24ccd3755a72401c android.hardware.wifi@1.0::IWifiIface
-ba5aa74f1ba714f0093864227923492808795bda6199c4ea0891322d27f8c931 android.hardware.wifi@1.0::IWifiNanIface
-325c94f3e1a565b56bbc74faddbd0ba7cb824f263dccf9dfff2daf62b86ed774 android.hardware.wifi@1.0::IWifiNanIfaceEventCallback
-c2c3f0372b41780fb6dfe83c022296806c2024d7046682fd201de5aa9b791c7a android.hardware.wifi@1.0::IWifiP2pIface
-766e9765f5c9c759b2a763c2288353fb5deff3389c2cc28f81d79c939704ce8b android.hardware.wifi@1.0::IWifiRttController
-72ab6f3e120cbf07aa6f8e87ca89112bdeb36b7fbb96bce5af3712323ab8b8e6 android.hardware.wifi@1.0::IWifiRttControllerEventCallback
-3b8093d39ef1e10e43c5538afbf5ff6e39b8d8168ebbe1998d993e89e25f14a5 android.hardware.wifi@1.0::IWifiStaIface
-7fbfc551c3e23c8b4398c3e16e452b516457e6921424a53474cbf373ca306fa9 android.hardware.wifi@1.0::IWifiStaIfaceEventCallback
-e20d5132d6d23e072c15de065b5e2aa13ff965031246a2c82581732bae56bf6d android.hardware.wifi@1.0::types
-f7e55c08187d8c855068a1ee3d0c8daeee7570292d96509c21a8756d4f5cfb9b android.hardware.wifi.supplicant@1.0::ISupplicant
-56b5c7267cb3d3337f44eb8b0b38ff4c6260dcc70e07687fcab94b1ccea8d159 android.hardware.wifi.supplicant@1.0::ISupplicantCallback
-35ba7bcdf18f24a866a7e5429548f06768bb20a257f75b10a397c4d825ef8438 android.hardware.wifi.supplicant@1.0::ISupplicantIface
-cda01008c06922fa37c1213e9bb831a109b3174532805616fb7161edc403866f android.hardware.wifi.supplicant@1.0::ISupplicantNetwork
-4907410338c5e8dbeec4b5edc2608ea323f5561945f8810af81810c47b019184 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIface
-8b63f5efa2e3be3a7cb8a428760d82285a4ab79bcbdea6ef90aa547555e582d4 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIfaceCallback
-56128f74560571b6777d59453f35c6b35693ee377e2a23c807708906928f09de android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetwork
-2067c22197bca9743dab66a6f561a8a8375c67b4f76aed05f776839499bd4c8f android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetworkCallback
-7752e1de93aaf5fed37011c219ac247069f6af320b0810daa98510584a10e7b4 android.hardware.wifi.supplicant@1.0::ISupplicantStaIface
-d781c8d7e7b3fe5cca8cf6e1d8806e770982ae5358c7816ed51b0f0ec272e70d android.hardware.wifi.supplicant@1.0::ISupplicantStaIfaceCallback
-b12ef0bdd8a4d247a8a6e960b227ed32383f2b0241f55d67fcea6eff6a6737fa android.hardware.wifi.supplicant@1.0::ISupplicantStaNetwork
-d8f0877ae1d321c1d884c7631dfe36cab0ec8a4b2863d4b687f85d3549a63bcc android.hardware.wifi.supplicant@1.0::ISupplicantStaNetworkCallback
-fe3c3c2f572b72f15f8594c538b0577bd5c28722c31879cfe6231330cddb6747 android.hardware.wifi.supplicant@1.0::types
-
-# ABI preserving changes to HALs released in Android O
-
-78589343d8ee2e1b155acad3fbdc7fcbb6af94491aee968b2383c21627264f8b android.hardware.radio@1.0::IRadioResponse
-c2c50ec74c87a583c683b4493f8f9f2e454a8d41c57af5b3eb88823a999f0ea4 android.hardware.radio@1.0::IRadioResponse
diff --git a/prebuilt_hashes/27.txt b/prebuilt_hashes/27.txt
deleted file mode 100644
index 23f2004..0000000
--- a/prebuilt_hashes/27.txt
+++ /dev/null
@@ -1,249 +0,0 @@
-# Do not change this file except to add new interfaces. Changing
-# pre-existing interfaces will fail VTS and break framework-only OTAs
-
-# HALs released in Android O
-
-f219c3b5b8c6cb1d659d4c7328f67246abfe1a8613f469826fd3b9ad090417a2 android.hardware.audio@2.0::IDevice
-4d579cae1cd87a783fd49233e10ce720ba183cfd1d5ccd80149e69de5c1c7362 android.hardware.audio@2.0::IDevicesFactory
-203e23f18011390b8cd10c303e0c16c4eebc8fa187e80e40d6be4624c2b0848a android.hardware.audio@2.0::IPrimaryDevice
-aaf93123deec336eb247ad8099849469a541ca0cf7c28c5f5336cebe1ee86748 android.hardware.audio@2.0::IStream
-0468c5723b0d44c5b451bdfa06153000c6f352fd3336e0ad2697127b04b766df android.hardware.audio@2.0::IStreamIn
-7296f7064fd3ab24082b43a1da34cc876268065310b785499fba5178d063680a android.hardware.audio@2.0::IStreamOut
-19d241d71c3e1140afba8140dcb57448446025a5fc03739788c4c25e9a98b6c2 android.hardware.audio@2.0::IStreamOutCallback
-c84da9f586087227daa3b96d42b4575326bccfd5bc8a2a5acf86d774f8ea2648 android.hardware.audio@2.0::types
-1305786c06e22b9b24ebde136054cc827b63c86d8bf4a136d6f7f54752b8566b android.hardware.audio.common@2.0::types
-fa8fbae3d1da3c264e4f3110728076abc09b4e65f12af6ae136367328de988ab android.hardware.audio.effect@2.0::IAcousticEchoCancelerEffect
-ca4752545d54547ff069eae161af7550cb5f5a7e8b60316ddd132a30906a68e7 android.hardware.audio.effect@2.0::IAutomaticGainControlEffect
-d2b8af988dc66f514d886bcee44b440d8034bc2a762f7161717ef3c956073067 android.hardware.audio.effect@2.0::IBassBoostEffect
-611bc09c75e796f3512b1ca6be508b0a9ba996759b8a2c60507784ff58076229 android.hardware.audio.effect@2.0::IDownmixEffect
-36a57369dfdc75180e8b64ae80b1970db8f6d9085dbff6ca931715038cc056e1 android.hardware.audio.effect@2.0::IEffect
-d2aa2df6d189c580f5be8460fa0ff4134d9c05a383f3204659baee426a6f0edf android.hardware.audio.effect@2.0::IEffectBufferProviderCallback
-217f9161983a48d3bf3faeb158f868aa8bf0ce25889e4ee3d2bab1a2e8d33e77 android.hardware.audio.effect@2.0::IEffectsFactory
-c2b38bc07991e880c83ca8cb88181411eeef708b8b936aedd2f2e0acade7df69 android.hardware.audio.effect@2.0::IEnvironmentalReverbEffect
-2ff9f9704be5f167745b4de790e9dafc3cc4719e2f6e2e5497085e679853cfe7 android.hardware.audio.effect@2.0::IEqualizerEffect
-c31447fb02dbc8b56c359941dad22f416511860173c5c5fd278d1bf2312b13de android.hardware.audio.effect@2.0::ILoudnessEnhancerEffect
-804831ca258802eb3eb65a0a7b5d5e3d37d4a15ba8c2836b4276eda98b47e1d0 android.hardware.audio.effect@2.0::INoiseSuppressionEffect
-778fd5b9837f481d8e47425b3e2a3bd0c6362a0b6870291518e2d863530fdb61 android.hardware.audio.effect@2.0::IPresetReverbEffect
-c93cb25a1a92d07aa80a617c01e8d22fc97bf8cefd3962b6a5be386ad4704d89 android.hardware.audio.effect@2.0::IVirtualizerEffect
-918f331780c9c7b04f2151a2e563aab088198ede8e6f865302ebaa13905bd9ce android.hardware.audio.effect@2.0::IVisualizerEffect
-4caad099f8fc00262b6c03ba41271808b37cea90ac98b534299bbf4ee823af02 android.hardware.audio.effect@2.0::types
-# android.hardware.automotive.* are unfrozen
-1fbdc1f852f8bd2e4a6c5cb30ac2b78668c98dce118a61762d4034ae859f43d8 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint
-aabb5c3c585592d71ee57b77298c14993d77914ddeaa64b2c5109a602b02ea47 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprintClientCallback
-1ec60d4efddae9a7b2469278a576967b4751e88de5b8d7e9df6eff6bc0da7bc9 android.hardware.biometrics.fingerprint@2.1::types
-347ce746815607567f5f3b53e4800998ca5ab9355141f0880fc0cf0c1fc5c355 android.hardware.bluetooth@1.0::IBluetoothHci
-835f41be2281bfb22f3e33c6fa870bde7bc21e37e5cfbaf9a36fff170632f754 android.hardware.bluetooth@1.0::IBluetoothHciCallbacks
-a8dfd0dbe463a3cdbcf1d985b38a28b3d93ba2ae5a1d1db4aaef4c38a5781b91 android.hardware.bluetooth@1.0::types
-7192d756aeba00aba32f4504981df8172ffca83e210c4838dabf295e53e93590 android.hardware.boot@1.0::IBootControl
-cebaa803b8e33807a0d69f46652b650ccb549e8f9b19d6becbbf26690e828b49 android.hardware.boot@1.0::types
-a98d49f23712a7cc327d1e0602d05f6f3ad32cfb5c74711d009c726611ee1c93 android.hardware.broadcastradio@1.0::IBroadcastRadio
-ed82579c0c165feaa12d0e33c06b3342ab41ec0a439247f202775e8369e46ef6 android.hardware.broadcastradio@1.0::IBroadcastRadioFactory
-da6ab32ee2793d2c86d3b603075d5383852b89d7eaa201861aa0473d418f3c7f android.hardware.broadcastradio@1.0::ITuner
-04d3ca022e25c308d9efd2e7eb77b3a7a206907cdc1b9ea9326340b377868172 android.hardware.broadcastradio@1.0::ITunerCallback
-bd42c8d7838cfed1998b49c39745dec116d2d6edc2c11a4c0399b8f3a1d1655a android.hardware.broadcastradio@1.0::types
-81164323115d6588e259e8319fddf7487adfa1f49ce60f7e80ba74e0783392a4 android.hardware.camera.common@1.0::types
-c1705e9d62438a1d955269965af915ae28e692bd480a3b1ce67056fef992d62f android.hardware.camera.device@1.0::ICameraDevice
-78e9b44cf8660bdc1e98dca07451804153824efcd28db208a62f5ad728f44076 android.hardware.camera.device@1.0::ICameraDeviceCallback
-28f0386ba86ddf41e53a8117b48a0328d7a4d2574213e89f4a1062398a244566 android.hardware.camera.device@1.0::ICameraDevicePreviewCallback
-4db48439ce9dde97f1cfb3d7408f6c737f621ac0f7494aeea35ed599bc2352a3 android.hardware.camera.device@1.0::types
-b32f9aeaf1c442195eb06ffc7600968c919d005b2718874f09c57287fae55918 android.hardware.camera.device@3.2::ICameraDevice
-63bfc3da0f2d2301f7a0508c7c2b9ffc521d4d545ee03718da70e9d6273b3b21 android.hardware.camera.device@3.2::ICameraDeviceCallback
-0fa3e1e64819283b8737fc4e5ab759f0cb4ac1a996e8a51cc4aa8025a457208e android.hardware.camera.device@3.2::ICameraDeviceSession
-030be3d2b159cbde7920485807140f6b6064ef4a5de4a40a6c4bc8d2c72f7cd3 android.hardware.camera.device@3.2::types
-5ba7947cee515d7a2359bfcbfb9678c1c3a768c288471919ac095b96ae6f3d40 android.hardware.camera.metadata@3.2::types
-f7e299d85033ac52d1095a35784fcfeaff43603f58c751e4153c85bbade3b330 android.hardware.camera.provider@2.4::ICameraProvider
-a501ca1aecd09f1b9fd70a9af84205430dbd49a808e8fa395d363b9902e6f58c android.hardware.camera.provider@2.4::ICameraProviderCallback
-7f5fe8f4f8a24037153c504d8b4d3313c2ce33d81c8c69fe5194ddd2d4080e72 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
-87beacc481897cf02fb1628d75e68133de6d74d4cffe582cda2f5e16bdd74516 android.hardware.configstore@1.0::types
-a5ae0fe8667f0b1af09b13e72d29600f4eb3853b257079c45a99b6f4a3360649 android.hardware.contexthub@1.0::IContexthub
-2ab3054c2d9302d8417ee7495353a2887fe338f913276f2eb41e80f11395ec2e android.hardware.contexthub@1.0::IContexthubCallback
-c3b2b37d561d31ea094411f251bf73bea334f4fe849a4390aef5e20bca6cadba android.hardware.contexthub@1.0::types
-df174c1871c864b4c79ca9f64aae7936d24a272eca3191a30458ca2b706dec79 android.hardware.drm@1.0::ICryptoFactory
-83639e90caeb996b0274e420de3cd556779de1ca48464b68eee799bef32b34cd android.hardware.drm@1.0::ICryptoPlugin
-1440cffdfaeb12830ac10ee6ffdb0f1083e701057b806df11fb4787b4c91e718 android.hardware.drm@1.0::IDrmFactory
-78ba33b108f620e6a0eec01ef654547e69a85754578ea4c9ef03ec205f16121c android.hardware.drm@1.0::IDrmPlugin
-701d9e51952172364e4ea70db9c397f08c3b4577ba33051f050a6cdd532de1b4 android.hardware.drm@1.0::IDrmPluginListener
-4238d62ad90df63aa338c6f1b6264c09c5a3706945d5c49d1189c0be1dc9e942 android.hardware.drm@1.0::types
-f07b1ee3ba11a2fc9f200421b2e1afb7c1854ee987000e45c987fb9507795055 android.hardware.dumpstate@1.0::IDumpstateDevice
-c9d318df7922bde3b265927b521ff5a965002826fc0cabfcaef52a56760f2d34 android.hardware.gatekeeper@1.0::IGatekeeper
-da13bd69282fb275767abb18704c57ff8038e6c139ad17157dc702810f70d06a android.hardware.gatekeeper@1.0::types
-37c7da4f823ec958dfa9c960e2d341c48f877e0bfa758f3fa9e2d9c1e1bd66d9 android.hardware.gnss@1.0::IAGnss
-7ec9afdb964bfb8369866913caf018f2636592885bcb558a65de2c5436ab4f60 android.hardware.gnss@1.0::IAGnssCallback
-d16e6a359be6963ea753d7138e84ecf2b93052097938938c4d36d7a47ea2e2ae android.hardware.gnss@1.0::IAGnssRil
-2f907708d74d94b1e121ed27651c9c72af65952d347b58ff07dac5d5d7a7f678 android.hardware.gnss@1.0::IAGnssRilCallback
-5ac7edad06d76064b882be161f3f9d9692a997ec72e9f36addb7fe8918f49992 android.hardware.gnss@1.0::IGnss
-b05c983c87c3376e145223688c3b541b5e11b827f211e38d5a31af1ca3a2e222 android.hardware.gnss@1.0::IGnssBatching
-4981d2d2c4e725c7544be0956099a91fc7bbc8048c563394158083fe924e651e android.hardware.gnss@1.0::IGnssBatchingCallback
-3cd22d92cc1f935150c5048310e84886f14eed2556e8f00636733d204045cc4f android.hardware.gnss@1.0::IGnssCallback
-175185a5eda87476193ca5461df75dd16d36664591e8130530dd8ef0eb2ddf6a android.hardware.gnss@1.0::IGnssConfiguration
-4542122b96fbf27101cb8222bafb76e7c8d032d977dd1058edd8e5881ca5752f android.hardware.gnss@1.0::IGnssDebug
-e6dd0c8416e523ab9cbd14d56ab6f016481a8aef3bc8a750051122d31075f6c7 android.hardware.gnss@1.0::IGnssGeofenceCallback
-f90e4ddc652706299d8e3d8ba18e0745c3bae9bf4d1be6bd06d9c1f50ec8d28a android.hardware.gnss@1.0::IGnssGeofencing
-9ea8987bb1089c8c5d7b67866575b866ef516045021d9efcc37c6352bce072a3 android.hardware.gnss@1.0::IGnssMeasurement
-cf20492673d6a423e4c2e87fdfb5a4c4a602431721978db852e246f258e25edb android.hardware.gnss@1.0::IGnssMeasurementCallback
-af85aa0f48ae99a39f4688c344e4419304f681f9af818a5c8d759286fc4418de android.hardware.gnss@1.0::IGnssNavigationMessage
-76b0874ea4c06b29f66418c59820f4286b3be9629cd872923d0dfbb602cd432d android.hardware.gnss@1.0::IGnssNavigationMessageCallback
-248bcf51da4273d64f367bf6877baef2feeaca365459842fd3c214a2dc6e0224 android.hardware.gnss@1.0::IGnssNi
-c781b7b125f68be5db8a8c3d412d526acdbdf77dcc592a4c0ed70b8ce4fe6c49 android.hardware.gnss@1.0::IGnssNiCallback
-c1142657de16fdb292a502372fe938614d65270ab8359217d6e13604fe4dbca4 android.hardware.gnss@1.0::IGnssXtra
-bd366b83d8d565d0e8bfabff3adfcab0259d75b4e2a9f8e1b91e11d1593a2ffb android.hardware.gnss@1.0::IGnssXtraCallback
-881bc2f94026784d194cffbff166c6e8bf911de4e02abe96fc7d89ec75b0574a android.hardware.gnss@1.0::types
-17971eb8a482893dadcfc16e0583f492d42a034ef95d9b0b709417af30838396 android.hardware.graphics.allocator@2.0::IAllocator
-60bf42a4898e4fb70dbd720b263aeafd7f35f5e1a5effeabb4d5d659878a5f18 android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer
-b8a75617b9ec12bea641f3a73d4025a33e8b9a2f9169dd46094af56adf9249c5 android.hardware.graphics.bufferqueue@1.0::IProducerListener
-4f6dedbcdd21c309dfc650acea81a096d6b242493ffe49c8d61bd3c43aad354e android.hardware.graphics.common@1.0::types
-b3aac6c3817f039964fcd62268274b3039e17bd7d0d5b40b4d1d1c7b19a1f866 android.hardware.graphics.composer@2.1::IComposer
-b19d00eb8a8b3b0034a0321f22e8f32162bf4c2aebbce6da22c025f56e459ea2 android.hardware.graphics.composer@2.1::IComposerCallback
-61ee43ffe6fb6dbe8b22dc17c51ff3d5ba703fc6029cba211f901f3d79c8a72d android.hardware.graphics.composer@2.1::IComposerClient
-1c98c2f5154345312ec054871792a2982ec5f3e2bc2abfb61a10c0b517978e20 android.hardware.graphics.composer@2.1::types
-a695898589e1ef15b2b2510f11edd6aafac9918d9cf8d74b4b6143b309dee542 android.hardware.graphics.mapper@2.0::IMapper
-28507d385a3dd224bf3c32f1bfd9f96092c4701b9c1cc66caa578fc3efc97877 android.hardware.graphics.mapper@2.0::types
-91e2ba3805c923f01fc1231ec9ff838942aee3346f2d7614ecc0caeadbe57ed4 android.hardware.health@1.0::IHealth
-1275aa2e8732909101b26aec49ed2285489e89d97b8610a8908b7868e35a3cc5 android.hardware.health@1.0::types
-3a8d3922e06e6d4f8e0befc6be78d0e9e07aed1585b3da6521bed406d25a9483 android.hardware.ir@1.0::IConsumerIr
-7090bd37912fcf723a12f4bc17783e3527577c4944805a028c296fd7a95bd682 android.hardware.ir@1.0::types
-cc7925a78c0ab022515f48840d3dae76f384ed3a1287abadcb461a5cd5396163 android.hardware.keymaster@3.0::IKeymasterDevice
-822998d7bb76f0cd719a409291434fcb56e6d50bc4780788bb157a3374d63b8c android.hardware.keymaster@3.0::types
-d4ed2f0e14f9e914d0b1275d2e0363192fe30aca9059c84edb5fad15995f9ec4 android.hardware.light@2.0::ILight
-d9584bfcaedd6e62cf337881748246b23e36cbc2bc3aa84c01b6a1e622061400 android.hardware.light@2.0::types
-16c0cf0f73de1e5208a95020c6c6474903e7094f76b2d782651afaca0e5fd86f android.hardware.media@1.0::types
-8bc2f5fdcad68856eb61a62fe4cc043fa064bb7f1dab95a71d1918ec1eef7b55 android.hardware.media.omx@1.0::IGraphicBufferSource
-0d3de9cd89d4718ea3b772f2d8b93be004feb3abb7e7dc5402e37047cc730d05 android.hardware.media.omx@1.0::IOmx
-32002e1c358c64de106c977a6dc6af7da27be4803a5bb66fd6f891a5ba0a1617 android.hardware.media.omx@1.0::IOmxBufferSource
-81ad8d8bb1cf6f41923cf11dd39354a8fe433db284a234cc675de7e75a82224c android.hardware.media.omx@1.0::IOmxNode
-494c0c8bf6065edc82ec127228ed19dd2243dc1c2f7d601c7c6be7b7015c1713 android.hardware.media.omx@1.0::IOmxObserver
-252c2fc50c78fd6de8365e5b60e5115119ace107db0b94b0b26815cbf3d2b64a android.hardware.media.omx@1.0::IOmxStore
-148c1b50b0958988373145ffdf5fa0e1b6534e0a2034a570e74b15c127cf7c5e android.hardware.media.omx@1.0::types
-c66902fe48d687ac6740a3e32ae55fb75532c48c36c6386461c2b4416ad2e0f1 android.hardware.memtrack@1.0::IMemtrack
-860bacd8b11a269c40567542b613fe4ca448d5cb4326d0058899e608e89dfca1 android.hardware.memtrack@1.0::types
-07ac2dc95270321ec7d4c33cd25e5085a057f47fe350d645af6f7a7a11e3cf57 android.hardware.nfc@1.0::INfc
-f2fe54426c07d67388d4774a60641ad4c0538f22eb6e1111722f231772655de6 android.hardware.nfc@1.0::INfcClientCallback
-9626fd18db113d709faf593a70caf19bd0980294d23c468c80c30186f9d298a6 android.hardware.nfc@1.0::types
-deee1dc4948f33af207e1008aba0f6cc07afb7900eab53f33192c8cac137eefc android.hardware.power@1.0::IPower
-efc83df3f962b93c7c0290d691d7d300dabe12683e2cde3591fb3c0beedce20f android.hardware.power@1.0::types
-9b5aa499ec3b4226f15f48f5ed08896e2fc0676f978c9e199c1da21daaf002a6 android.hardware.radio@1.0::IRadio
-5c8efbb9c451a59737ed2c6c20230aae4745839ca01d8088d6dcc9020e52d2c5 android.hardware.radio@1.0::IRadioIndication
-69f6b4b8ec40ca02ccc7bb8227a097135c20c00bd94c822e421cd9af1267252c android.hardware.radio@1.0::IRadioResponse
-de3ab9f73b1073cd677b19d886fb927e9381b30161a704712d2b30f875873f5c android.hardware.radio@1.0::ISap
-d183e406ef0897df2117a9dde384e8e6ea4fa6ab1c7f6f28e65b87011218c9ea android.hardware.radio@1.0::ISapCallback
-96986fbd22f0e6ca752e1fcdc0a64bda213995a81f5f36bc4faf3532d9306b97 android.hardware.radio@1.0::types
-00f70085d6fae1d482fb700a3fd42ed475384c95b51c9269b9ae5037b74ad4dd android.hardware.radio.deprecated@1.0::IOemHook
-06837b6d7e843cfa9cd20fed4070feca7a9b5c81a9ed643bf7d06803455a9816 android.hardware.radio.deprecated@1.0::IOemHookIndication
-6fd4874f0eddd4626a27658fd94fad526c317f3563439e79718bdb1a3a2309d5 android.hardware.radio.deprecated@1.0::IOemHookResponse
-6983a2cafe39d5c57dfdc1743055fb0f757a0df8c78e00423d5e1810836927e1 android.hardware.renderscript@1.0::IContext
-7f9417a0ccf78ea042ec7a8ac8e3750346d4d9d7e5ae01b1b35fde303f47c24d android.hardware.renderscript@1.0::IDevice
-fc6f325b266b32353f7d1534fbe58e0d368265a12b77fa396fb556e8c443f739 android.hardware.renderscript@1.0::types
-89585ff541c319de4091a5a0b687dd526ac81c6382ffd7b979a4164b3d7419a6 android.hardware.sensors@1.0::ISensors
-e04ab978fc28f4c515f4a75617dfda8607733a64f13666beeb0e604a07a39333 android.hardware.sensors@1.0::types
-5befc019cbe94953661e2cdb95e3cf64f5e565c29403e1c2daecc2be44e0a55c android.hardware.soundtrigger@2.0::ISoundTriggerHw
-d7ec5f612a5e0a59ea4f2b61317e208ff56dd50920fd4eb441e0cbc8f97e4f49 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
-5bee9e70f7e5ec7ee252883b28f98f12b59960f4c2a0b4cc9a4526e4669ebcd4 android.hardware.soundtrigger@2.0::types
-97f1ec446043bc5a6645b74529a6276496bdb35e0aee41eda55cb92d51eb7802 android.hardware.thermal@1.0::IThermal
-84965a6908bceb4ef51546bf8731f309f1ea9d09a0177dcc7974132e523dd6d2 android.hardware.thermal@1.0::types
-938850621c3c5ef426a4b88e752ba99b3559037e782a3d938604f3aef5cc0f1b android.hardware.tv.cec@1.0::IHdmiCec
-e75b6eea711d36fac678bce072b3cec6544b27fa9f4cd903999404e5c110ca10 android.hardware.tv.cec@1.0::IHdmiCecCallback
-6e25f8dbfadb668e1d4ec80eb9acd95d8bc9e0a240c36d27e662adb440314b95 android.hardware.tv.cec@1.0::types
-0cafa3c8388e9631916d2d800f78decbcec2904f11415b32c71a31d9a51ebf79 android.hardware.tv.input@1.0::ITvInput
-dee83e5c864fbbee8f02448d0800be32f06856386b9f907bc5d952208c9434f9 android.hardware.tv.input@1.0::ITvInputCallback
-07aab30410b612381234dca6d453d4ab96f2e536368715717c6e28101b9851b5 android.hardware.tv.input@1.0::types
-4ef57499273f38bdbdd0c15e56ee7a4bc5f18a5644092170a531df3541d9e015 android.hardware.usb@1.0::IUsb
-4be7881e411ba42784bf5b7354c14ae0cf161004d39433aaecaab0d19ea99354 android.hardware.usb@1.0::IUsbCallback
-f7e6e747910a3cd0a35846141e3b990a6a612a297b2b70ccd5740b646a450a8c android.hardware.usb@1.0::types
-06ea64cc3565777f3b259e400ffa7100d07f3827ad9357b0c5d3c651384e5553 android.hardware.vibrator@1.0::IVibrator
-0fecd34ae64f32eff6aa615fd662349242c0b8b6e303ef05a7cb5776c732f413 android.hardware.vibrator@1.0::types
-4b962968a7df4ab104d1315d66a0a7348a713fecbb5d2c1b23688494458f37ce android.hardware.vr@1.0::IVr
-b9be36719a8ad534000a51ea07be91be94c405bf1e038ae825acf65087ffd378 android.hardware.wifi@1.0::IWifi
-ee0224ee18813506d9d6f13d8c8e4679f053c290a443a52a7c52a5d3c852262b android.hardware.wifi@1.0::IWifiApIface
-f3eecc489deb4c74892f59eb7adb769063bd5c354ac132b626a5f42b363d36bc android.hardware.wifi@1.0::IWifiChip
-a1b988377645a58e5e2542ca2bad4e17c21a4a389213d05de2f0e32d57b7d339 android.hardware.wifi@1.0::IWifiChipEventCallback
-5ed6760ce77e84bc6c49d1acb3f7d8117c9176b3f06514bc44ad3af84c80dcfe android.hardware.wifi@1.0::IWifiEventCallback
-6b9ad43a5efbe6ca214f751e22ce43cf5cd4d5d5f2cba80f24ccd3755a72401c android.hardware.wifi@1.0::IWifiIface
-ba5aa74f1ba714f0093864227923492808795bda6199c4ea0891322d27f8c931 android.hardware.wifi@1.0::IWifiNanIface
-325c94f3e1a565b56bbc74faddbd0ba7cb824f263dccf9dfff2daf62b86ed774 android.hardware.wifi@1.0::IWifiNanIfaceEventCallback
-c2c3f0372b41780fb6dfe83c022296806c2024d7046682fd201de5aa9b791c7a android.hardware.wifi@1.0::IWifiP2pIface
-766e9765f5c9c759b2a763c2288353fb5deff3389c2cc28f81d79c939704ce8b android.hardware.wifi@1.0::IWifiRttController
-72ab6f3e120cbf07aa6f8e87ca89112bdeb36b7fbb96bce5af3712323ab8b8e6 android.hardware.wifi@1.0::IWifiRttControllerEventCallback
-3b8093d39ef1e10e43c5538afbf5ff6e39b8d8168ebbe1998d993e89e25f14a5 android.hardware.wifi@1.0::IWifiStaIface
-7fbfc551c3e23c8b4398c3e16e452b516457e6921424a53474cbf373ca306fa9 android.hardware.wifi@1.0::IWifiStaIfaceEventCallback
-e20d5132d6d23e072c15de065b5e2aa13ff965031246a2c82581732bae56bf6d android.hardware.wifi@1.0::types
-f7e55c08187d8c855068a1ee3d0c8daeee7570292d96509c21a8756d4f5cfb9b android.hardware.wifi.supplicant@1.0::ISupplicant
-56b5c7267cb3d3337f44eb8b0b38ff4c6260dcc70e07687fcab94b1ccea8d159 android.hardware.wifi.supplicant@1.0::ISupplicantCallback
-35ba7bcdf18f24a866a7e5429548f06768bb20a257f75b10a397c4d825ef8438 android.hardware.wifi.supplicant@1.0::ISupplicantIface
-cda01008c06922fa37c1213e9bb831a109b3174532805616fb7161edc403866f android.hardware.wifi.supplicant@1.0::ISupplicantNetwork
-4907410338c5e8dbeec4b5edc2608ea323f5561945f8810af81810c47b019184 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIface
-8b63f5efa2e3be3a7cb8a428760d82285a4ab79bcbdea6ef90aa547555e582d4 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIfaceCallback
-56128f74560571b6777d59453f35c6b35693ee377e2a23c807708906928f09de android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetwork
-2067c22197bca9743dab66a6f561a8a8375c67b4f76aed05f776839499bd4c8f android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetworkCallback
-7752e1de93aaf5fed37011c219ac247069f6af320b0810daa98510584a10e7b4 android.hardware.wifi.supplicant@1.0::ISupplicantStaIface
-d781c8d7e7b3fe5cca8cf6e1d8806e770982ae5358c7816ed51b0f0ec272e70d android.hardware.wifi.supplicant@1.0::ISupplicantStaIfaceCallback
-b12ef0bdd8a4d247a8a6e960b227ed32383f2b0241f55d67fcea6eff6a6737fa android.hardware.wifi.supplicant@1.0::ISupplicantStaNetwork
-d8f0877ae1d321c1d884c7631dfe36cab0ec8a4b2863d4b687f85d3549a63bcc android.hardware.wifi.supplicant@1.0::ISupplicantStaNetworkCallback
-fe3c3c2f572b72f15f8594c538b0577bd5c28722c31879cfe6231330cddb6747 android.hardware.wifi.supplicant@1.0::types
-
-# ABI preserving changes to HALs during Android O MR1 (Initial Set)
-
-# android.hardware.automotive.* are unfrozen
-150a338ce11fcec70757c9675d83cf6a5d7b40d0c812741b91671fecce59eac9 android.hardware.broadcastradio@1.0::types
-dc7e6d4f537b9943e27edc4f86c5a03bb643b18f18f866f8c3c71c0ac4ea8cbc android.hardware.broadcastradio@1.0::types
-760485232f6cce07f8bb05e3475509956996b702f77415ee5bff05e2ec5a5bcc android.hardware.dumpstate@1.0::IDumpstateDevice
-e822cb7f4a1bdd45689c5e92ccd19a2201c20b771bd4b2ec1ae627e324591f9d android.hardware.radio@1.0::IRadioResponse
-6e69adb24d7c0b0ca3a54a38c49a5625b161b3f5d5f7d6fda0befdbbfc8e9e06 android.hardware.radio@1.0::IRadioResponse
-28e929b453df3d9f5060af2764e6cdb123ddb893e3e86923c877f6ff7e5f02c9 android.hardware.wifi@1.0::types
-df1d7b27e644bfed0a4f606a8c44d35d45cafce82c7c648494c8a25c7cd4a949 android.hardware.wifi@1.0::types
-
-# HALs released in Android O MR1 (Initial Set)
-
-4b65763663a94a3920134011691f8fbb42ccb7b7795589efddc049a9106047d6 android.hardware.oemlock@1.0::IOemLock
-e02cd3722cb5e8fa51179f5defacb4f7866f903c9c7c51dc01a3148473a71525 android.hardware.oemlock@1.0::types
-224f9d22a367a0016f09b6dc676f53f1446697d9dc747163032329e5da552de5 android.hardware.power@1.1::IPower
-574fd9758b7cab4922c72cc5a9f36d1cd48ffd3425fdd776426653280d3d4138 android.hardware.power@1.1::types
-f79edf50a378a9c9bb737f93f205dab91b4c63ea49723afc6f856c138203ea81 android.hardware.radio@1.1::IRadio
-fcc5c8c88b85a9f63fba67d9e674da466c72a98ca287f343fb5721d098713f86 android.hardware.radio@1.1::IRadioIndication
-50f27e8c7ec009d5d4418b2ce8392b940bbf052ecc1d7251285f332485a5ba4e android.hardware.radio@1.1::IRadioResponse
-be981148c95c0089f3ae92854f0e7ae999d308e927db3e065f12a4fabe07852f android.hardware.radio@1.1::ISap
-d8d6bf7b4d36c04ce587df75953c3f723cfbe71c896c1aa8ab6478eae126723d android.hardware.radio@1.1::types
-d8aae01606bfd34bf2fb9a59cadc016f46f318e56cddb8f15a945c5b3c1222bc android.hardware.tetheroffload.config@1.0::IOffloadConfig
-447b00306bc95a7aafec1d660f6f3e9f76ac8bc0353193435e5579ab833da619 android.hardware.tetheroffload.control@1.0::IOffloadControl
-07658829339d75962016e00ed81b005ad29fca7ac12ad3bc3ccd86b08d94c2d3 android.hardware.tetheroffload.control@1.0::ITetheringOffloadCallback
-0df5b0178af15c53cdce8fcf8ca14035e8e08db4fa76fdc12009ddbe0b53626b android.hardware.tetheroffload.control@1.0::types
-b30ef02ef26ff804e2f6acf1201bc141b59e134e6a0338562284491102cb13e3 android.hardware.usb@1.1::IUsb
-13a580e35af01270a1e9774177c51db51d8672e6139ba00851e654e68a4d7dff android.hardware.usb@1.1::IUsbCallback
-f0ed667288908c08fced570bd1f3c4a0f236aa927938e805f0d9fece525da81e android.hardware.usb@1.1::types
-f95a1e85612f2d0d616eacd2eb63c52d10dfa889f165df57697c30e1f47b4785 android.hardware.vibrator@1.1::IVibrator
-246fb9d9e2b4800aeb0adc3cdbaa15d0321ebab54b7bd1ab87da5b67c7b0b064 android.hardware.vibrator@1.1::types
-9bc43413b80cd0c59a022e93da1448dcb82dd10c6dd31932df4659e4bdcb1368 android.hardware.weaver@1.0::IWeaver
-7728b0393a2ed9796537d4165c7d95407e9d8cb447a647b545fdfe06a28689e7 android.hardware.weaver@1.0::types
-bb7c96762d0aa3ddb874c8815bacdd3cbc8fb87ea2f82b928bc29e24a3593055 android.hardware.wifi.offload@1.0::IOffload
-c3354ab0d381a236c12dc486ad4b6bec28c979d26748b4661f12ede36f392808 android.hardware.wifi.offload@1.0::IOffloadCallback
-b18caefefcc765092412285d776234fcf213b73bdf07ae1b67a5f71b2d2464e3 android.hardware.wifi.offload@1.0::types
-c26473e2e4a00af43e28a0ddf9002e5062a7d0940429e5efb6e5513a8abcb75c android.hardware.wifi@1.1::IWifi
-b056e1defab4071584214584057d0bc73a613081bf1152590549649d4582c13c android.hardware.wifi@1.1::IWifiChip
-
-# ABI preserving changes to HALs during Android O MR1 (Final Set)
-2d833aeed0cd1d59437aca210be590a953cf32bcb6683cd63d089762a643fb49 android.hardware.radio@1.0::IRadioResponse
-05aa3de6130a9788fdb6f4d3cc57c3ea90f067e77a5e09d6a772ec7f6bca33d2 android.hardware.radio@1.1::IRadioResponse
-
-# HALs released in Android O MR1 (Final Set)
-044cb039378b8a0e36f40ff1e6ce04dc0d339da02095f968d5062a051e99d108 android.hardware.broadcastradio@1.1::types
-c9699483f8cefe4f9b39b4b9609b76cab2dd1659a06188056b45797d337d4256 android.hardware.broadcastradio@1.1::IBroadcastRadio
-b5d62dcd663fc4fcc977b252af59b333043bdfe73de2f11fe6d6a8bf438a0f92 android.hardware.broadcastradio@1.1::IBroadcastRadioFactory
-bc7e054a6e93adebedff345aeed44549be89e6b1b6ffe071ff47a61de764b232 android.hardware.broadcastradio@1.1::ITuner
-e9139fc755be578693f17c8cd1e27c75f412cfc722157bab5bf03ee68896e31d android.hardware.broadcastradio@1.1::ITunerCallback
-63929c99e5755d9e09d9e0fd2527391fbb1609dda0508f5933b7943b92ae0fbc android.hardware.camera.device@3.3::types
-bbcfc3f748b078f6a66c4e228084a679d30bd61bfde8bb7a91efd507b91c1bfd android.hardware.camera.device@3.3::ICameraDeviceSession
-4a6998cd6793a3f9f03989c29d662589b1bc9d38826c6698c6c17864f7a814f5 android.hardware.cas@1.0::types
-0e656ba1bac11461a17096ef752b69d24b000d820ef5652f0150a0f9731d54c2 android.hardware.cas@1.0::ICas
-b80e1456b81f80032d0de7cb45652ac15af11e7474d520d757481ecaad796dff android.hardware.cas@1.0::ICasListener
-a432d6d9200248dc2126827bcd6cdea31dd65eff39b939f64585d27d915a5857 android.hardware.cas@1.0::IDescramblerBase
-86ba9c03978b79a742e990420bc5ced0673d25a939f82572996bef92621e2014 android.hardware.cas@1.0::IMediaCasService
-503da837d1a67cbdb7c08a033e927e5430ae1b159d98bf72c6336b4dcc5e76f5 android.hardware.cas.native@1.0::types
-619600109232ed64b827c8a11beed8070b1827ae464547d7aa146cf0473b4bca android.hardware.cas.native@1.0::IDescrambler
-0a159f81359cd4f71bbe00972ee8403ea79351fb7c0cd48be72ebb3e424dbaef android.hardware.radio@1.0::types
-09342041e17c429fce0034b9096d17849122111436a5f0053e7e59500e1cb89c android.hardware.media.omx@1.0::IOmxStore
-246a56d37d57a47224562c9d077b4a2886ce6242b9311bd98a17325944c280d7 android.hardware.neuralnetworks@1.0::types
-93eb3757ceaf21590fa4cd1d4a7dfe3b3794af5396100a6d25630879352abce9 android.hardware.neuralnetworks@1.0::IDevice
-f66f9a38541bf92001d3adcce678cd7e3da2262124befb460b1c9aea9492813b android.hardware.neuralnetworks@1.0::IExecutionCallback
-953607822954435874f4b81686440a604e2a88cdd2d9164c6293f3d5772510d7 android.hardware.neuralnetworks@1.0::IPreparedModel
-73e03573494ba96f0e711ab7f1956c5b2d54c3da690cd7ecf4d6d0f287447730 android.hardware.neuralnetworks@1.0::IPreparedModelCallback
-f4945e397b5dea41bb64518dfde59be71245d8a125fd1e0acffeb57ac7b08fed android.hardware.thermal@1.1::IThermal
-c8bc853546dd55584611def2a9fa1d99f657e3366c976d2f60fe6b8aa6d2cb87 android.hardware.thermal@1.1::IThermalCallback
diff --git a/prebuilt_hashes/28.txt b/prebuilt_hashes/28.txt
deleted file mode 100644
index 8a45ca5..0000000
--- a/prebuilt_hashes/28.txt
+++ /dev/null
@@ -1,384 +0,0 @@
-# Do not change this file except to add new interfaces. Changing
-# pre-existing interfaces will fail VTS and break framework-only OTAs
-
-# HALs released in Android O
-
-f219c3b5b8c6cb1d659d4c7328f67246abfe1a8613f469826fd3b9ad090417a2 android.hardware.audio@2.0::IDevice
-4d579cae1cd87a783fd49233e10ce720ba183cfd1d5ccd80149e69de5c1c7362 android.hardware.audio@2.0::IDevicesFactory
-203e23f18011390b8cd10c303e0c16c4eebc8fa187e80e40d6be4624c2b0848a android.hardware.audio@2.0::IPrimaryDevice
-aaf93123deec336eb247ad8099849469a541ca0cf7c28c5f5336cebe1ee86748 android.hardware.audio@2.0::IStream
-0468c5723b0d44c5b451bdfa06153000c6f352fd3336e0ad2697127b04b766df android.hardware.audio@2.0::IStreamIn
-7296f7064fd3ab24082b43a1da34cc876268065310b785499fba5178d063680a android.hardware.audio@2.0::IStreamOut
-19d241d71c3e1140afba8140dcb57448446025a5fc03739788c4c25e9a98b6c2 android.hardware.audio@2.0::IStreamOutCallback
-c84da9f586087227daa3b96d42b4575326bccfd5bc8a2a5acf86d774f8ea2648 android.hardware.audio@2.0::types
-1305786c06e22b9b24ebde136054cc827b63c86d8bf4a136d6f7f54752b8566b android.hardware.audio.common@2.0::types
-fa8fbae3d1da3c264e4f3110728076abc09b4e65f12af6ae136367328de988ab android.hardware.audio.effect@2.0::IAcousticEchoCancelerEffect
-ca4752545d54547ff069eae161af7550cb5f5a7e8b60316ddd132a30906a68e7 android.hardware.audio.effect@2.0::IAutomaticGainControlEffect
-d2b8af988dc66f514d886bcee44b440d8034bc2a762f7161717ef3c956073067 android.hardware.audio.effect@2.0::IBassBoostEffect
-611bc09c75e796f3512b1ca6be508b0a9ba996759b8a2c60507784ff58076229 android.hardware.audio.effect@2.0::IDownmixEffect
-36a57369dfdc75180e8b64ae80b1970db8f6d9085dbff6ca931715038cc056e1 android.hardware.audio.effect@2.0::IEffect
-d2aa2df6d189c580f5be8460fa0ff4134d9c05a383f3204659baee426a6f0edf android.hardware.audio.effect@2.0::IEffectBufferProviderCallback
-217f9161983a48d3bf3faeb158f868aa8bf0ce25889e4ee3d2bab1a2e8d33e77 android.hardware.audio.effect@2.0::IEffectsFactory
-c2b38bc07991e880c83ca8cb88181411eeef708b8b936aedd2f2e0acade7df69 android.hardware.audio.effect@2.0::IEnvironmentalReverbEffect
-2ff9f9704be5f167745b4de790e9dafc3cc4719e2f6e2e5497085e679853cfe7 android.hardware.audio.effect@2.0::IEqualizerEffect
-c31447fb02dbc8b56c359941dad22f416511860173c5c5fd278d1bf2312b13de android.hardware.audio.effect@2.0::ILoudnessEnhancerEffect
-804831ca258802eb3eb65a0a7b5d5e3d37d4a15ba8c2836b4276eda98b47e1d0 android.hardware.audio.effect@2.0::INoiseSuppressionEffect
-778fd5b9837f481d8e47425b3e2a3bd0c6362a0b6870291518e2d863530fdb61 android.hardware.audio.effect@2.0::IPresetReverbEffect
-c93cb25a1a92d07aa80a617c01e8d22fc97bf8cefd3962b6a5be386ad4704d89 android.hardware.audio.effect@2.0::IVirtualizerEffect
-918f331780c9c7b04f2151a2e563aab088198ede8e6f865302ebaa13905bd9ce android.hardware.audio.effect@2.0::IVisualizerEffect
-4caad099f8fc00262b6c03ba41271808b37cea90ac98b534299bbf4ee823af02 android.hardware.audio.effect@2.0::types
-1fbdc1f852f8bd2e4a6c5cb30ac2b78668c98dce118a61762d4034ae859f43d8 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprint
-aabb5c3c585592d71ee57b77298c14993d77914ddeaa64b2c5109a602b02ea47 android.hardware.biometrics.fingerprint@2.1::IBiometricsFingerprintClientCallback
-1ec60d4efddae9a7b2469278a576967b4751e88de5b8d7e9df6eff6bc0da7bc9 android.hardware.biometrics.fingerprint@2.1::types
-347ce746815607567f5f3b53e4800998ca5ab9355141f0880fc0cf0c1fc5c355 android.hardware.bluetooth@1.0::IBluetoothHci
-835f41be2281bfb22f3e33c6fa870bde7bc21e37e5cfbaf9a36fff170632f754 android.hardware.bluetooth@1.0::IBluetoothHciCallbacks
-a8dfd0dbe463a3cdbcf1d985b38a28b3d93ba2ae5a1d1db4aaef4c38a5781b91 android.hardware.bluetooth@1.0::types
-7192d756aeba00aba32f4504981df8172ffca83e210c4838dabf295e53e93590 android.hardware.boot@1.0::IBootControl
-cebaa803b8e33807a0d69f46652b650ccb549e8f9b19d6becbbf26690e828b49 android.hardware.boot@1.0::types
-a98d49f23712a7cc327d1e0602d05f6f3ad32cfb5c74711d009c726611ee1c93 android.hardware.broadcastradio@1.0::IBroadcastRadio
-ed82579c0c165feaa12d0e33c06b3342ab41ec0a439247f202775e8369e46ef6 android.hardware.broadcastradio@1.0::IBroadcastRadioFactory
-da6ab32ee2793d2c86d3b603075d5383852b89d7eaa201861aa0473d418f3c7f android.hardware.broadcastradio@1.0::ITuner
-04d3ca022e25c308d9efd2e7eb77b3a7a206907cdc1b9ea9326340b377868172 android.hardware.broadcastradio@1.0::ITunerCallback
-bd42c8d7838cfed1998b49c39745dec116d2d6edc2c11a4c0399b8f3a1d1655a android.hardware.broadcastradio@1.0::types
-81164323115d6588e259e8319fddf7487adfa1f49ce60f7e80ba74e0783392a4 android.hardware.camera.common@1.0::types
-c1705e9d62438a1d955269965af915ae28e692bd480a3b1ce67056fef992d62f android.hardware.camera.device@1.0::ICameraDevice
-78e9b44cf8660bdc1e98dca07451804153824efcd28db208a62f5ad728f44076 android.hardware.camera.device@1.0::ICameraDeviceCallback
-28f0386ba86ddf41e53a8117b48a0328d7a4d2574213e89f4a1062398a244566 android.hardware.camera.device@1.0::ICameraDevicePreviewCallback
-4db48439ce9dde97f1cfb3d7408f6c737f621ac0f7494aeea35ed599bc2352a3 android.hardware.camera.device@1.0::types
-b32f9aeaf1c442195eb06ffc7600968c919d005b2718874f09c57287fae55918 android.hardware.camera.device@3.2::ICameraDevice
-63bfc3da0f2d2301f7a0508c7c2b9ffc521d4d545ee03718da70e9d6273b3b21 android.hardware.camera.device@3.2::ICameraDeviceCallback
-0fa3e1e64819283b8737fc4e5ab759f0cb4ac1a996e8a51cc4aa8025a457208e android.hardware.camera.device@3.2::ICameraDeviceSession
-030be3d2b159cbde7920485807140f6b6064ef4a5de4a40a6c4bc8d2c72f7cd3 android.hardware.camera.device@3.2::types
-5ba7947cee515d7a2359bfcbfb9678c1c3a768c288471919ac095b96ae6f3d40 android.hardware.camera.metadata@3.2::types
-f7e299d85033ac52d1095a35784fcfeaff43603f58c751e4153c85bbade3b330 android.hardware.camera.provider@2.4::ICameraProvider
-a501ca1aecd09f1b9fd70a9af84205430dbd49a808e8fa395d363b9902e6f58c android.hardware.camera.provider@2.4::ICameraProviderCallback
-7f5fe8f4f8a24037153c504d8b4d3313c2ce33d81c8c69fe5194ddd2d4080e72 android.hardware.configstore@1.0::ISurfaceFlingerConfigs
-87beacc481897cf02fb1628d75e68133de6d74d4cffe582cda2f5e16bdd74516 android.hardware.configstore@1.0::types
-a5ae0fe8667f0b1af09b13e72d29600f4eb3853b257079c45a99b6f4a3360649 android.hardware.contexthub@1.0::IContexthub
-2ab3054c2d9302d8417ee7495353a2887fe338f913276f2eb41e80f11395ec2e android.hardware.contexthub@1.0::IContexthubCallback
-c3b2b37d561d31ea094411f251bf73bea334f4fe849a4390aef5e20bca6cadba android.hardware.contexthub@1.0::types
-df174c1871c864b4c79ca9f64aae7936d24a272eca3191a30458ca2b706dec79 android.hardware.drm@1.0::ICryptoFactory
-83639e90caeb996b0274e420de3cd556779de1ca48464b68eee799bef32b34cd android.hardware.drm@1.0::ICryptoPlugin
-1440cffdfaeb12830ac10ee6ffdb0f1083e701057b806df11fb4787b4c91e718 android.hardware.drm@1.0::IDrmFactory
-78ba33b108f620e6a0eec01ef654547e69a85754578ea4c9ef03ec205f16121c android.hardware.drm@1.0::IDrmPlugin
-701d9e51952172364e4ea70db9c397f08c3b4577ba33051f050a6cdd532de1b4 android.hardware.drm@1.0::IDrmPluginListener
-4238d62ad90df63aa338c6f1b6264c09c5a3706945d5c49d1189c0be1dc9e942 android.hardware.drm@1.0::types
-f07b1ee3ba11a2fc9f200421b2e1afb7c1854ee987000e45c987fb9507795055 android.hardware.dumpstate@1.0::IDumpstateDevice
-c9d318df7922bde3b265927b521ff5a965002826fc0cabfcaef52a56760f2d34 android.hardware.gatekeeper@1.0::IGatekeeper
-da13bd69282fb275767abb18704c57ff8038e6c139ad17157dc702810f70d06a android.hardware.gatekeeper@1.0::types
-37c7da4f823ec958dfa9c960e2d341c48f877e0bfa758f3fa9e2d9c1e1bd66d9 android.hardware.gnss@1.0::IAGnss
-7ec9afdb964bfb8369866913caf018f2636592885bcb558a65de2c5436ab4f60 android.hardware.gnss@1.0::IAGnssCallback
-d16e6a359be6963ea753d7138e84ecf2b93052097938938c4d36d7a47ea2e2ae android.hardware.gnss@1.0::IAGnssRil
-2f907708d74d94b1e121ed27651c9c72af65952d347b58ff07dac5d5d7a7f678 android.hardware.gnss@1.0::IAGnssRilCallback
-5ac7edad06d76064b882be161f3f9d9692a997ec72e9f36addb7fe8918f49992 android.hardware.gnss@1.0::IGnss
-b05c983c87c3376e145223688c3b541b5e11b827f211e38d5a31af1ca3a2e222 android.hardware.gnss@1.0::IGnssBatching
-4981d2d2c4e725c7544be0956099a91fc7bbc8048c563394158083fe924e651e android.hardware.gnss@1.0::IGnssBatchingCallback
-3cd22d92cc1f935150c5048310e84886f14eed2556e8f00636733d204045cc4f android.hardware.gnss@1.0::IGnssCallback
-175185a5eda87476193ca5461df75dd16d36664591e8130530dd8ef0eb2ddf6a android.hardware.gnss@1.0::IGnssConfiguration
-4542122b96fbf27101cb8222bafb76e7c8d032d977dd1058edd8e5881ca5752f android.hardware.gnss@1.0::IGnssDebug
-e6dd0c8416e523ab9cbd14d56ab6f016481a8aef3bc8a750051122d31075f6c7 android.hardware.gnss@1.0::IGnssGeofenceCallback
-f90e4ddc652706299d8e3d8ba18e0745c3bae9bf4d1be6bd06d9c1f50ec8d28a android.hardware.gnss@1.0::IGnssGeofencing
-9ea8987bb1089c8c5d7b67866575b866ef516045021d9efcc37c6352bce072a3 android.hardware.gnss@1.0::IGnssMeasurement
-cf20492673d6a423e4c2e87fdfb5a4c4a602431721978db852e246f258e25edb android.hardware.gnss@1.0::IGnssMeasurementCallback
-af85aa0f48ae99a39f4688c344e4419304f681f9af818a5c8d759286fc4418de android.hardware.gnss@1.0::IGnssNavigationMessage
-76b0874ea4c06b29f66418c59820f4286b3be9629cd872923d0dfbb602cd432d android.hardware.gnss@1.0::IGnssNavigationMessageCallback
-248bcf51da4273d64f367bf6877baef2feeaca365459842fd3c214a2dc6e0224 android.hardware.gnss@1.0::IGnssNi
-c781b7b125f68be5db8a8c3d412d526acdbdf77dcc592a4c0ed70b8ce4fe6c49 android.hardware.gnss@1.0::IGnssNiCallback
-c1142657de16fdb292a502372fe938614d65270ab8359217d6e13604fe4dbca4 android.hardware.gnss@1.0::IGnssXtra
-bd366b83d8d565d0e8bfabff3adfcab0259d75b4e2a9f8e1b91e11d1593a2ffb android.hardware.gnss@1.0::IGnssXtraCallback
-881bc2f94026784d194cffbff166c6e8bf911de4e02abe96fc7d89ec75b0574a android.hardware.gnss@1.0::types
-17971eb8a482893dadcfc16e0583f492d42a034ef95d9b0b709417af30838396 android.hardware.graphics.allocator@2.0::IAllocator
-60bf42a4898e4fb70dbd720b263aeafd7f35f5e1a5effeabb4d5d659878a5f18 android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer
-b8a75617b9ec12bea641f3a73d4025a33e8b9a2f9169dd46094af56adf9249c5 android.hardware.graphics.bufferqueue@1.0::IProducerListener
-4f6dedbcdd21c309dfc650acea81a096d6b242493ffe49c8d61bd3c43aad354e android.hardware.graphics.common@1.0::types
-b3aac6c3817f039964fcd62268274b3039e17bd7d0d5b40b4d1d1c7b19a1f866 android.hardware.graphics.composer@2.1::IComposer
-b19d00eb8a8b3b0034a0321f22e8f32162bf4c2aebbce6da22c025f56e459ea2 android.hardware.graphics.composer@2.1::IComposerCallback
-61ee43ffe6fb6dbe8b22dc17c51ff3d5ba703fc6029cba211f901f3d79c8a72d android.hardware.graphics.composer@2.1::IComposerClient
-1c98c2f5154345312ec054871792a2982ec5f3e2bc2abfb61a10c0b517978e20 android.hardware.graphics.composer@2.1::types
-a695898589e1ef15b2b2510f11edd6aafac9918d9cf8d74b4b6143b309dee542 android.hardware.graphics.mapper@2.0::IMapper
-28507d385a3dd224bf3c32f1bfd9f96092c4701b9c1cc66caa578fc3efc97877 android.hardware.graphics.mapper@2.0::types
-91e2ba3805c923f01fc1231ec9ff838942aee3346f2d7614ecc0caeadbe57ed4 android.hardware.health@1.0::IHealth
-1275aa2e8732909101b26aec49ed2285489e89d97b8610a8908b7868e35a3cc5 android.hardware.health@1.0::types
-3a8d3922e06e6d4f8e0befc6be78d0e9e07aed1585b3da6521bed406d25a9483 android.hardware.ir@1.0::IConsumerIr
-7090bd37912fcf723a12f4bc17783e3527577c4944805a028c296fd7a95bd682 android.hardware.ir@1.0::types
-cc7925a78c0ab022515f48840d3dae76f384ed3a1287abadcb461a5cd5396163 android.hardware.keymaster@3.0::IKeymasterDevice
-822998d7bb76f0cd719a409291434fcb56e6d50bc4780788bb157a3374d63b8c android.hardware.keymaster@3.0::types
-d4ed2f0e14f9e914d0b1275d2e0363192fe30aca9059c84edb5fad15995f9ec4 android.hardware.light@2.0::ILight
-d9584bfcaedd6e62cf337881748246b23e36cbc2bc3aa84c01b6a1e622061400 android.hardware.light@2.0::types
-16c0cf0f73de1e5208a95020c6c6474903e7094f76b2d782651afaca0e5fd86f android.hardware.media@1.0::types
-8bc2f5fdcad68856eb61a62fe4cc043fa064bb7f1dab95a71d1918ec1eef7b55 android.hardware.media.omx@1.0::IGraphicBufferSource
-0d3de9cd89d4718ea3b772f2d8b93be004feb3abb7e7dc5402e37047cc730d05 android.hardware.media.omx@1.0::IOmx
-32002e1c358c64de106c977a6dc6af7da27be4803a5bb66fd6f891a5ba0a1617 android.hardware.media.omx@1.0::IOmxBufferSource
-81ad8d8bb1cf6f41923cf11dd39354a8fe433db284a234cc675de7e75a82224c android.hardware.media.omx@1.0::IOmxNode
-494c0c8bf6065edc82ec127228ed19dd2243dc1c2f7d601c7c6be7b7015c1713 android.hardware.media.omx@1.0::IOmxObserver
-252c2fc50c78fd6de8365e5b60e5115119ace107db0b94b0b26815cbf3d2b64a android.hardware.media.omx@1.0::IOmxStore
-148c1b50b0958988373145ffdf5fa0e1b6534e0a2034a570e74b15c127cf7c5e android.hardware.media.omx@1.0::types
-c66902fe48d687ac6740a3e32ae55fb75532c48c36c6386461c2b4416ad2e0f1 android.hardware.memtrack@1.0::IMemtrack
-860bacd8b11a269c40567542b613fe4ca448d5cb4326d0058899e608e89dfca1 android.hardware.memtrack@1.0::types
-07ac2dc95270321ec7d4c33cd25e5085a057f47fe350d645af6f7a7a11e3cf57 android.hardware.nfc@1.0::INfc
-f2fe54426c07d67388d4774a60641ad4c0538f22eb6e1111722f231772655de6 android.hardware.nfc@1.0::INfcClientCallback
-9626fd18db113d709faf593a70caf19bd0980294d23c468c80c30186f9d298a6 android.hardware.nfc@1.0::types
-deee1dc4948f33af207e1008aba0f6cc07afb7900eab53f33192c8cac137eefc android.hardware.power@1.0::IPower
-efc83df3f962b93c7c0290d691d7d300dabe12683e2cde3591fb3c0beedce20f android.hardware.power@1.0::types
-9b5aa499ec3b4226f15f48f5ed08896e2fc0676f978c9e199c1da21daaf002a6 android.hardware.radio@1.0::IRadio
-5c8efbb9c451a59737ed2c6c20230aae4745839ca01d8088d6dcc9020e52d2c5 android.hardware.radio@1.0::IRadioIndication
-69f6b4b8ec40ca02ccc7bb8227a097135c20c00bd94c822e421cd9af1267252c android.hardware.radio@1.0::IRadioResponse
-de3ab9f73b1073cd677b19d886fb927e9381b30161a704712d2b30f875873f5c android.hardware.radio@1.0::ISap
-d183e406ef0897df2117a9dde384e8e6ea4fa6ab1c7f6f28e65b87011218c9ea android.hardware.radio@1.0::ISapCallback
-96986fbd22f0e6ca752e1fcdc0a64bda213995a81f5f36bc4faf3532d9306b97 android.hardware.radio@1.0::types
-00f70085d6fae1d482fb700a3fd42ed475384c95b51c9269b9ae5037b74ad4dd android.hardware.radio.deprecated@1.0::IOemHook
-06837b6d7e843cfa9cd20fed4070feca7a9b5c81a9ed643bf7d06803455a9816 android.hardware.radio.deprecated@1.0::IOemHookIndication
-6fd4874f0eddd4626a27658fd94fad526c317f3563439e79718bdb1a3a2309d5 android.hardware.radio.deprecated@1.0::IOemHookResponse
-6983a2cafe39d5c57dfdc1743055fb0f757a0df8c78e00423d5e1810836927e1 android.hardware.renderscript@1.0::IContext
-7f9417a0ccf78ea042ec7a8ac8e3750346d4d9d7e5ae01b1b35fde303f47c24d android.hardware.renderscript@1.0::IDevice
-fc6f325b266b32353f7d1534fbe58e0d368265a12b77fa396fb556e8c443f739 android.hardware.renderscript@1.0::types
-89585ff541c319de4091a5a0b687dd526ac81c6382ffd7b979a4164b3d7419a6 android.hardware.sensors@1.0::ISensors
-e04ab978fc28f4c515f4a75617dfda8607733a64f13666beeb0e604a07a39333 android.hardware.sensors@1.0::types
-5befc019cbe94953661e2cdb95e3cf64f5e565c29403e1c2daecc2be44e0a55c android.hardware.soundtrigger@2.0::ISoundTriggerHw
-d7ec5f612a5e0a59ea4f2b61317e208ff56dd50920fd4eb441e0cbc8f97e4f49 android.hardware.soundtrigger@2.0::ISoundTriggerHwCallback
-5bee9e70f7e5ec7ee252883b28f98f12b59960f4c2a0b4cc9a4526e4669ebcd4 android.hardware.soundtrigger@2.0::types
-97f1ec446043bc5a6645b74529a6276496bdb35e0aee41eda55cb92d51eb7802 android.hardware.thermal@1.0::IThermal
-84965a6908bceb4ef51546bf8731f309f1ea9d09a0177dcc7974132e523dd6d2 android.hardware.thermal@1.0::types
-938850621c3c5ef426a4b88e752ba99b3559037e782a3d938604f3aef5cc0f1b android.hardware.tv.cec@1.0::IHdmiCec
-e75b6eea711d36fac678bce072b3cec6544b27fa9f4cd903999404e5c110ca10 android.hardware.tv.cec@1.0::IHdmiCecCallback
-6e25f8dbfadb668e1d4ec80eb9acd95d8bc9e0a240c36d27e662adb440314b95 android.hardware.tv.cec@1.0::types
-0cafa3c8388e9631916d2d800f78decbcec2904f11415b32c71a31d9a51ebf79 android.hardware.tv.input@1.0::ITvInput
-dee83e5c864fbbee8f02448d0800be32f06856386b9f907bc5d952208c9434f9 android.hardware.tv.input@1.0::ITvInputCallback
-07aab30410b612381234dca6d453d4ab96f2e536368715717c6e28101b9851b5 android.hardware.tv.input@1.0::types
-4ef57499273f38bdbdd0c15e56ee7a4bc5f18a5644092170a531df3541d9e015 android.hardware.usb@1.0::IUsb
-4be7881e411ba42784bf5b7354c14ae0cf161004d39433aaecaab0d19ea99354 android.hardware.usb@1.0::IUsbCallback
-f7e6e747910a3cd0a35846141e3b990a6a612a297b2b70ccd5740b646a450a8c android.hardware.usb@1.0::types
-06ea64cc3565777f3b259e400ffa7100d07f3827ad9357b0c5d3c651384e5553 android.hardware.vibrator@1.0::IVibrator
-0fecd34ae64f32eff6aa615fd662349242c0b8b6e303ef05a7cb5776c732f413 android.hardware.vibrator@1.0::types
-4b962968a7df4ab104d1315d66a0a7348a713fecbb5d2c1b23688494458f37ce android.hardware.vr@1.0::IVr
-b9be36719a8ad534000a51ea07be91be94c405bf1e038ae825acf65087ffd378 android.hardware.wifi@1.0::IWifi
-ee0224ee18813506d9d6f13d8c8e4679f053c290a443a52a7c52a5d3c852262b android.hardware.wifi@1.0::IWifiApIface
-f3eecc489deb4c74892f59eb7adb769063bd5c354ac132b626a5f42b363d36bc android.hardware.wifi@1.0::IWifiChip
-a1b988377645a58e5e2542ca2bad4e17c21a4a389213d05de2f0e32d57b7d339 android.hardware.wifi@1.0::IWifiChipEventCallback
-5ed6760ce77e84bc6c49d1acb3f7d8117c9176b3f06514bc44ad3af84c80dcfe android.hardware.wifi@1.0::IWifiEventCallback
-6b9ad43a5efbe6ca214f751e22ce43cf5cd4d5d5f2cba80f24ccd3755a72401c android.hardware.wifi@1.0::IWifiIface
-ba5aa74f1ba714f0093864227923492808795bda6199c4ea0891322d27f8c931 android.hardware.wifi@1.0::IWifiNanIface
-325c94f3e1a565b56bbc74faddbd0ba7cb824f263dccf9dfff2daf62b86ed774 android.hardware.wifi@1.0::IWifiNanIfaceEventCallback
-c2c3f0372b41780fb6dfe83c022296806c2024d7046682fd201de5aa9b791c7a android.hardware.wifi@1.0::IWifiP2pIface
-766e9765f5c9c759b2a763c2288353fb5deff3389c2cc28f81d79c939704ce8b android.hardware.wifi@1.0::IWifiRttController
-72ab6f3e120cbf07aa6f8e87ca89112bdeb36b7fbb96bce5af3712323ab8b8e6 android.hardware.wifi@1.0::IWifiRttControllerEventCallback
-3b8093d39ef1e10e43c5538afbf5ff6e39b8d8168ebbe1998d993e89e25f14a5 android.hardware.wifi@1.0::IWifiStaIface
-7fbfc551c3e23c8b4398c3e16e452b516457e6921424a53474cbf373ca306fa9 android.hardware.wifi@1.0::IWifiStaIfaceEventCallback
-e20d5132d6d23e072c15de065b5e2aa13ff965031246a2c82581732bae56bf6d android.hardware.wifi@1.0::types
-f7e55c08187d8c855068a1ee3d0c8daeee7570292d96509c21a8756d4f5cfb9b android.hardware.wifi.supplicant@1.0::ISupplicant
-56b5c7267cb3d3337f44eb8b0b38ff4c6260dcc70e07687fcab94b1ccea8d159 android.hardware.wifi.supplicant@1.0::ISupplicantCallback
-35ba7bcdf18f24a866a7e5429548f06768bb20a257f75b10a397c4d825ef8438 android.hardware.wifi.supplicant@1.0::ISupplicantIface
-cda01008c06922fa37c1213e9bb831a109b3174532805616fb7161edc403866f android.hardware.wifi.supplicant@1.0::ISupplicantNetwork
-4907410338c5e8dbeec4b5edc2608ea323f5561945f8810af81810c47b019184 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIface
-8b63f5efa2e3be3a7cb8a428760d82285a4ab79bcbdea6ef90aa547555e582d4 android.hardware.wifi.supplicant@1.0::ISupplicantP2pIfaceCallback
-56128f74560571b6777d59453f35c6b35693ee377e2a23c807708906928f09de android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetwork
-2067c22197bca9743dab66a6f561a8a8375c67b4f76aed05f776839499bd4c8f android.hardware.wifi.supplicant@1.0::ISupplicantP2pNetworkCallback
-7752e1de93aaf5fed37011c219ac247069f6af320b0810daa98510584a10e7b4 android.hardware.wifi.supplicant@1.0::ISupplicantStaIface
-d781c8d7e7b3fe5cca8cf6e1d8806e770982ae5358c7816ed51b0f0ec272e70d android.hardware.wifi.supplicant@1.0::ISupplicantStaIfaceCallback
-b12ef0bdd8a4d247a8a6e960b227ed32383f2b0241f55d67fcea6eff6a6737fa android.hardware.wifi.supplicant@1.0::ISupplicantStaNetwork
-d8f0877ae1d321c1d884c7631dfe36cab0ec8a4b2863d4b687f85d3549a63bcc android.hardware.wifi.supplicant@1.0::ISupplicantStaNetworkCallback
-fe3c3c2f572b72f15f8594c538b0577bd5c28722c31879cfe6231330cddb6747 android.hardware.wifi.supplicant@1.0::types
-
-# ABI preserving changes to HALs during Android O MR1 (Initial Set)
-
-150a338ce11fcec70757c9675d83cf6a5d7b40d0c812741b91671fecce59eac9 android.hardware.broadcastradio@1.0::types
-dc7e6d4f537b9943e27edc4f86c5a03bb643b18f18f866f8c3c71c0ac4ea8cbc android.hardware.broadcastradio@1.0::types
-760485232f6cce07f8bb05e3475509956996b702f77415ee5bff05e2ec5a5bcc android.hardware.dumpstate@1.0::IDumpstateDevice
-78589343d8ee2e1b155acad3fbdc7fcbb6af94491aee968b2383c21627264f8b android.hardware.radio@1.0::IRadioResponse # Available in Android O, b/68061860
-e822cb7f4a1bdd45689c5e92ccd19a2201c20b771bd4b2ec1ae627e324591f9d android.hardware.radio@1.0::IRadioResponse
-6e69adb24d7c0b0ca3a54a38c49a5625b161b3f5d5f7d6fda0befdbbfc8e9e06 android.hardware.radio@1.0::IRadioResponse
-c2c50ec74c87a583c683b4493f8f9f2e454a8d41c57af5b3eb88823a999f0ea4 android.hardware.radio@1.0::IRadioResponse # Added for b/65230472 for Android O
-4922dd58e89a03181ed1c48a6e118e47633b73b11090bdfed5aa920d25a7592b android.hardware.radio@1.0::IRadioResponse # Added for b/65230472 for Android O DR
-28e929b453df3d9f5060af2764e6cdb123ddb893e3e86923c877f6ff7e5f02c9 android.hardware.wifi@1.0::types
-df1d7b27e644bfed0a4f606a8c44d35d45cafce82c7c648494c8a25c7cd4a949 android.hardware.wifi@1.0::types
-
-# HALs released in Android O MR1 (Initial Set)
-
-4b65763663a94a3920134011691f8fbb42ccb7b7795589efddc049a9106047d6 android.hardware.oemlock@1.0::IOemLock
-e02cd3722cb5e8fa51179f5defacb4f7866f903c9c7c51dc01a3148473a71525 android.hardware.oemlock@1.0::types
-224f9d22a367a0016f09b6dc676f53f1446697d9dc747163032329e5da552de5 android.hardware.power@1.1::IPower
-574fd9758b7cab4922c72cc5a9f36d1cd48ffd3425fdd776426653280d3d4138 android.hardware.power@1.1::types
-f79edf50a378a9c9bb737f93f205dab91b4c63ea49723afc6f856c138203ea81 android.hardware.radio@1.1::IRadio
-fcc5c8c88b85a9f63fba67d9e674da466c72a98ca287f343fb5721d098713f86 android.hardware.radio@1.1::IRadioIndication
-50f27e8c7ec009d5d4418b2ce8392b940bbf052ecc1d7251285f332485a5ba4e android.hardware.radio@1.1::IRadioResponse
-be981148c95c0089f3ae92854f0e7ae999d308e927db3e065f12a4fabe07852f android.hardware.radio@1.1::ISap
-d8d6bf7b4d36c04ce587df75953c3f723cfbe71c896c1aa8ab6478eae126723d android.hardware.radio@1.1::types
-d8aae01606bfd34bf2fb9a59cadc016f46f318e56cddb8f15a945c5b3c1222bc android.hardware.tetheroffload.config@1.0::IOffloadConfig
-447b00306bc95a7aafec1d660f6f3e9f76ac8bc0353193435e5579ab833da619 android.hardware.tetheroffload.control@1.0::IOffloadControl
-07658829339d75962016e00ed81b005ad29fca7ac12ad3bc3ccd86b08d94c2d3 android.hardware.tetheroffload.control@1.0::ITetheringOffloadCallback
-0df5b0178af15c53cdce8fcf8ca14035e8e08db4fa76fdc12009ddbe0b53626b android.hardware.tetheroffload.control@1.0::types
-b30ef02ef26ff804e2f6acf1201bc141b59e134e6a0338562284491102cb13e3 android.hardware.usb@1.1::IUsb
-13a580e35af01270a1e9774177c51db51d8672e6139ba00851e654e68a4d7dff android.hardware.usb@1.1::IUsbCallback
-f0ed667288908c08fced570bd1f3c4a0f236aa927938e805f0d9fece525da81e android.hardware.usb@1.1::types
-f95a1e85612f2d0d616eacd2eb63c52d10dfa889f165df57697c30e1f47b4785 android.hardware.vibrator@1.1::IVibrator
-246fb9d9e2b4800aeb0adc3cdbaa15d0321ebab54b7bd1ab87da5b67c7b0b064 android.hardware.vibrator@1.1::types
-9bc43413b80cd0c59a022e93da1448dcb82dd10c6dd31932df4659e4bdcb1368 android.hardware.weaver@1.0::IWeaver
-7728b0393a2ed9796537d4165c7d95407e9d8cb447a647b545fdfe06a28689e7 android.hardware.weaver@1.0::types
-bb7c96762d0aa3ddb874c8815bacdd3cbc8fb87ea2f82b928bc29e24a3593055 android.hardware.wifi.offload@1.0::IOffload
-c3354ab0d381a236c12dc486ad4b6bec28c979d26748b4661f12ede36f392808 android.hardware.wifi.offload@1.0::IOffloadCallback
-b18caefefcc765092412285d776234fcf213b73bdf07ae1b67a5f71b2d2464e3 android.hardware.wifi.offload@1.0::types
-c26473e2e4a00af43e28a0ddf9002e5062a7d0940429e5efb6e5513a8abcb75c android.hardware.wifi@1.1::IWifi
-b056e1defab4071584214584057d0bc73a613081bf1152590549649d4582c13c android.hardware.wifi@1.1::IWifiChip
-
-# ABI preserving changes to HALs during Android O MR1 (Final Set)
-09342041e17c429fce0034b9096d17849122111436a5f0053e7e59500e1cb89c android.hardware.media.omx@1.0::IOmxStore
-2d833aeed0cd1d59437aca210be590a953cf32bcb6683cd63d089762a643fb49 android.hardware.radio@1.0::IRadioResponse
-0a159f81359cd4f71bbe00972ee8403ea79351fb7c0cd48be72ebb3e424dbaef android.hardware.radio@1.0::types
-05aa3de6130a9788fdb6f4d3cc57c3ea90f067e77a5e09d6a772ec7f6bca33d2 android.hardware.radio@1.1::IRadioResponse
-
-# HALs released in Android O MR1 (Final Set)
-044cb039378b8a0e36f40ff1e6ce04dc0d339da02095f968d5062a051e99d108 android.hardware.broadcastradio@1.1::types
-c9699483f8cefe4f9b39b4b9609b76cab2dd1659a06188056b45797d337d4256 android.hardware.broadcastradio@1.1::IBroadcastRadio
-b5d62dcd663fc4fcc977b252af59b333043bdfe73de2f11fe6d6a8bf438a0f92 android.hardware.broadcastradio@1.1::IBroadcastRadioFactory
-bc7e054a6e93adebedff345aeed44549be89e6b1b6ffe071ff47a61de764b232 android.hardware.broadcastradio@1.1::ITuner
-e9139fc755be578693f17c8cd1e27c75f412cfc722157bab5bf03ee68896e31d android.hardware.broadcastradio@1.1::ITunerCallback
-63929c99e5755d9e09d9e0fd2527391fbb1609dda0508f5933b7943b92ae0fbc android.hardware.camera.device@3.3::types
-bbcfc3f748b078f6a66c4e228084a679d30bd61bfde8bb7a91efd507b91c1bfd android.hardware.camera.device@3.3::ICameraDeviceSession
-4a6998cd6793a3f9f03989c29d662589b1bc9d38826c6698c6c17864f7a814f5 android.hardware.cas@1.0::types
-0e656ba1bac11461a17096ef752b69d24b000d820ef5652f0150a0f9731d54c2 android.hardware.cas@1.0::ICas
-b80e1456b81f80032d0de7cb45652ac15af11e7474d520d757481ecaad796dff android.hardware.cas@1.0::ICasListener
-a432d6d9200248dc2126827bcd6cdea31dd65eff39b939f64585d27d915a5857 android.hardware.cas@1.0::IDescramblerBase
-86ba9c03978b79a742e990420bc5ced0673d25a939f82572996bef92621e2014 android.hardware.cas@1.0::IMediaCasService
-503da837d1a67cbdb7c08a033e927e5430ae1b159d98bf72c6336b4dcc5e76f5 android.hardware.cas.native@1.0::types
-619600109232ed64b827c8a11beed8070b1827ae464547d7aa146cf0473b4bca android.hardware.cas.native@1.0::IDescrambler
-93eb3757ceaf21590fa4cd1d4a7dfe3b3794af5396100a6d25630879352abce9 android.hardware.neuralnetworks@1.0::IDevice
-f66f9a38541bf92001d3adcce678cd7e3da2262124befb460b1c9aea9492813b android.hardware.neuralnetworks@1.0::IExecutionCallback
-953607822954435874f4b81686440a604e2a88cdd2d9164c6293f3d5772510d7 android.hardware.neuralnetworks@1.0::IPreparedModel
-73e03573494ba96f0e711ab7f1956c5b2d54c3da690cd7ecf4d6d0f287447730 android.hardware.neuralnetworks@1.0::IPreparedModelCallback
-246a56d37d57a47224562c9d077b4a2886ce6242b9311bd98a17325944c280d7 android.hardware.neuralnetworks@1.0::types
-f4945e397b5dea41bb64518dfde59be71245d8a125fd1e0acffeb57ac7b08fed android.hardware.thermal@1.1::IThermal
-c8bc853546dd55584611def2a9fa1d99f657e3366c976d2f60fe6b8aa6d2cb87 android.hardware.thermal@1.1::IThermalCallback
-
-# ABI preserving changes to HALs during Android P
-9e7a0b650d0e461ece2cfec0e1072abf8676f592b41a7fb48f01e88fc3c8f780 android.hardware.broadcastradio@1.0::types
-cf72ff5a52bfa4d08e9e1000cf3ab5952a2d280c7f13cdad5ab7905c08050766 android.hardware.camera.metadata@3.2::types
-3902efc42097cba55f0655aa389e052ea70164e99ced1a6d1ef53dafc13f7650 android.hardware.camera.provider@2.4::ICameraProvider
-6fa9804a17a8bb7923a56bd10493a5483c20007e4c9026fd04287bee7c945a8c android.hardware.gnss@1.0::IGnssCallback
-fb92e2b40f8e9d494e8fd3b4ac18499a3216342e7cff160714c3bbf3660b6e79 android.hardware.gnss@1.0::IGnssConfiguration
-251594ea9b27447bfa005ebd806e58fb0ae4aad84a69938129c9800ec0c64eda android.hardware.gnss@1.0::IGnssMeasurementCallback
-4e7169919d24fbe5573e5bcd683d0bd7abf553a4e6c34c41f9dfc1e12050db07 android.hardware.gnss@1.0::IGnssNavigationMessageCallback
-190ea4898809de6cf379afe318f5fa9564686157b24d9a2d7f5698b0c977d8b2 android.hardware.graphics.bufferqueue@1.0::IGraphicBufferProducer
-25892789b50eb673506b6c5a2cdab5d9aa428d41608aab10280cc898538b524a android.hardware.graphics.composer@2.1::IComposerClient
-e205dd30f5ff99445b706a901de8ebc46c379e9d7c1921d6a327ed2082cfa83d android.hardware.graphics.composer@2.1::types
-a46251718abfada458dc64c41ce94915757bf6c87cfa2d9e99cfb01fa8e32331 android.hardware.graphics.mapper@2.0::IMapper
-bd33ac23c57b4a07632691d2191bc2c93930f57e62f4ccf459748fdaa5c0f480 android.hardware.graphics.mapper@2.0::types
-ad8a28ca3a5549fb9bc24cf5f80ac8f660cc27be885210d76266780aa52ddb8d android.hardware.keymaster@3.0::types
-5804ca86611d72e5481f022b3a0c1b334217f2e4988dad25730c42af2d1f4d1c android.hardware.neuralnetworks@1.0::IDevice
-12e8dca4ab7d8aadd0ef8f1b438021938e2396139e85db2ed65783b08800aa52 android.hardware.neuralnetworks@1.0::IExecutionCallback
-86b77e06da756a76aa3685be88765852dd982a86d8c90b8b4fc1130ed4184c8f android.hardware.neuralnetworks@1.0::types
-d4840db8efabdf1e4b344fc981cd36e5fe81a39aff6e199f6d06c1c8da413efd android.hardware.radio@1.0::types
-f96cbc59dfe16c8d0c2a7e06db24d8738a6328b6e90f7b8e1640ea2b4600debd android.hardware.radio@1.1::ISap
-2d86929794795e5c70f4fdb5073485fd05835c9c6f496116687c3d9f32e6df3e android.hardware.radio@1.2::ISap
-905a4af79c8329b39d8b11b08f015137216bb078b427b6986f32884a04bc1bec android.hardware.tv.cec@1.0::types
-aebcd9ff2da05c9d4c439916f40dfd219ba7629919007cb981ebf150064b4f82 android.hardware.usb@1.1::IUsb
-e29fb1941b40a990676f8e9c676a38761defd890b81a9c034608eb7ba6496023 android.hardware.wifi@1.0::IWifiP2pIface
-b280c4704dfcc548a9bf127b59b7c3578f460c50cce70a06b66fe0df8b27cff0 android.hardware.wifi@1.0::types
-
-# HALs released in Android P
-5860cf040a3d5d771967ecf648b00d06876a7120da985ee2b3e95d01f634dd20 android.hardware.audio@4.0::IDevice
-cf82a0249e918fdc657e189895e92d60af0491868477e82cdc30f6cab0ca2c65 android.hardware.audio@4.0::IDevicesFactory
-be3dc9baed45a0d330152eca3ca24fa419b375b20a41644c88d4fb46b72784d2 android.hardware.audio@4.0::IPrimaryDevice
-3e3acb70c4e6c7d578f511f4a44ee764ab9126f887a3bf65d523c42e40012bf6 android.hardware.audio@4.0::IStream
-d5de64e66b95f135dd42492250a309134b8227203ef3524440798c66b6f5a392 android.hardware.audio@4.0::IStreamIn
-888ac906461327fa0bd93854d5109be8c292a33afdb467164826970a8bd5b789 android.hardware.audio@4.0::IStreamOut
-15f6ae78e73344c8e7d68847ef03caec64fcd9f951bbcf59957d1712c247fcff android.hardware.audio@4.0::IStreamOutCallback
-61f0eaa4d08547d039e9b1dd7c82abe2c004286d1b9b8153c2491ff46a8a63ca android.hardware.audio@4.0::types
-5d47a2ad2c136b8aba067dd45bb10d0ad390dd76340764154f580658f98f4fe6 android.hardware.audio.common@4.0::types
-b04b6b364938b80008e61fa2e318bc299622433e57c2e1f6cfba332a3f6e3f15 android.hardware.audio.effect@4.0::IAcousticEchoCancelerEffect
-1c17d4ece5c8ba3f7a646a305ee0dd109b0d51372e1bd585812e513cd40e1852 android.hardware.audio.effect@4.0::IAutomaticGainControlEffect
-34174259fe6fbb1bb14e7103e097f2f25529271a676687845b2f55d6d0d9d617 android.hardware.audio.effect@4.0::IBassBoostEffect
-7a18e9bd0163f3784448f6e24be0db75f877e2f0f9bd0d7ec427f1c34b382c0b android.hardware.audio.effect@4.0::IDownmixEffect
-bac81bffbe2661d5b6839087d2dd3a27eded66e60c6c76d35c68d54014cd5c06 android.hardware.audio.effect@4.0::IEffect
-65f0bcf9e498b26f3266ad10cf513a6c2b5906cc49f9db4bc5c7d3ba11a72e05 android.hardware.audio.effect@4.0::IEffectBufferProviderCallback
-5a746e81175489eb2371b88864c36c9bb63bc64ef799fae74cd96003b013c0d1 android.hardware.audio.effect@4.0::IEffectsFactory
-839980c7c5be79da6b95fdb9354a62b04407b4b084749b7a21d2c340773d7638 android.hardware.audio.effect@4.0::IEnvironmentalReverbEffect
-2805fbdac7cff050a1c095b9276bb41ac02a3b7b354336817487eb9a4b6bb462 android.hardware.audio.effect@4.0::IEqualizerEffect
-a91b547f5922f39fe4231d97fac1c3825c1c1b0c8ef7a5136689ceed37e8bfe9 android.hardware.audio.effect@4.0::ILoudnessEnhancerEffect
-1145f5b921ddec184fda5bdc87487b46f2a89cd9f42cc882bbb3a54f4ac80466 android.hardware.audio.effect@4.0::INoiseSuppressionEffect
-3661fa0623056922fdc4235ac5a9c91a2d066ab6f1ab4297e3b240fe302ba500 android.hardware.audio.effect@4.0::IPresetReverbEffect
-e88e520f8c98a62fccd8d5316c6687808f775de145d1405a7a9a66587ee6a001 android.hardware.audio.effect@4.0::IVirtualizerEffect
-fe28829dab10d171783b79ac9cc45412739f8ff275e90228d7c6370ef189b859 android.hardware.audio.effect@4.0::IVisualizerEffect
-21c8a702579356480236c6851b5b2c16b9bd369ce12bdd6ffdc4626a89f34f73  android.hardware.audio.effect@4.0::types
-42a06dc288f61b0690580f3d37b30b663c31d74d50bb58d0772386b550d5faab android.hardware.authsecret@1.0::IAuthSecret
-32cc50cc2a7658ec613c0c2dd2accbf6a05113b749852879e818b8b7b438db19 android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioHost
-ff4be64d7992f8bec97dff37f35450e79b3430c61f85f54322ce45bef229dc3b android.hardware.bluetooth.a2dp@1.0::IBluetoothAudioOffload
-27f22d2e873e6201f9620cf4d8e2facb25bd0dd30a2b911e441b4600d560fa62 android.hardware.bluetooth.a2dp@1.0::types
-3d8ed67d807e9f15d0708390a416bee00920f6a22196c104cc9e443c8d217df8 android.hardware.broadcastradio@2.0::IAnnouncementListener
-44017c42e6f4d8cb30f07eb1da04540a98736a336ac28c7e0ed2e69e1589f8d1 android.hardware.broadcastradio@2.0::IBroadcastRadio
-e5f4960290b4f3089163dd43251e1a032c81e9bdb796e75a87fc7c5810c262b3 android.hardware.broadcastradio@2.0::ICloseHandle
-af24b87ca8b8f02fcde205e47db6a9609fc7e9d77d73e694ec8f9c508ca19575 android.hardware.broadcastradio@2.0::ITunerCallback
-d70464c517a4a1b5167730843775a97f455102919e945b04f717b9da390c0f39 android.hardware.broadcastradio@2.0::ITunerSession
-2afa59ebf8145e7fbc090cf49605c27280c07d4178d47cd7f9d82b3b95a47af0 android.hardware.broadcastradio@2.0::types
-4fb0725c36ed4f77a42b42e3f18d8b5f7919cb62b90098b23143a555aa7dd96d android.hardware.camera.device@3.4::ICameraDeviceCallback
-812fa66aa10ba0cba27cfddc2fd7f0ee27a8ab65a1f15aa79fdad97d403e6a14 android.hardware.camera.device@3.4::ICameraDeviceSession
-cc288f1f78d1e643eb3d3dbc16e1401d44033d8e6856761f5156814a29986ec7 android.hardware.camera.device@3.4::types
-f9278c8beb9d42d96e26d73ecabe1dff1d7e2fb301ab7f737d93e5ffae8d3312 android.hardware.camera.metadata@3.3::types
-f858091b10f7d5927be60573c06df4805275d37226bbb41a732190bfb81457ec android.hardware.configstore@1.1::ISurfaceFlingerConfigs
-5b0fb9842f8b0eb3730b93c30a7925290ab44763ab86bb493bfa58d0f2eeb369 android.hardware.configstore@1.1::types
-1a46aeae45b7a0e47f79b7207300532986f9d9cd7060779afc7a529f54d712ab android.hardware.confirmationui@1.0::IConfirmationResultCallback
-6d8347ff3cd7de471065ac3e8e68385073630cdeebe9f8fa58cb91cf44436c95 android.hardware.confirmationui@1.0::IConfirmationUI
-a3ff916784dce87a56c757ab5c86433f0cdf562280999a5f978a6e8a0f3f19e7 android.hardware.confirmationui@1.0::types
-1fbf2d7e383632216aaaa1d972a21a618f55659263d2e6f0b309e3cb323b4b63 android.hardware.drm@1.1::ICryptoFactory
-7877ff8e4c1e48b825e6e5e66d050288e5656ed535c61cc7830a92ed4a9e1990 android.hardware.drm@1.1::IDrmFactory
-fef2f0ebde7704548fb203df46673ceb342272fc4fa9d0af25a980d2584a36e7 android.hardware.drm@1.1::IDrmPlugin
-5047a346ecce239404b9020959f60dd467318e9c17b290a6386bc3894df62c3c android.hardware.drm@1.1::types
-a830336ac8627d6432cfafb1b884343ad9f885dee0a5323e380e6d3c519156b8 android.hardware.gnss@1.1::IGnss
-8ad55bc35bb3a83e65c018bdfde7ae5ebc749ff2bf6b79412ded0bc6c89b97d8 android.hardware.gnss@1.1::IGnssCallback
-3c5183d7506010be57e0f748e3640fc2ded1ba955784b6256ba427f4c399591c android.hardware.gnss@1.1::IGnssConfiguration
-1a07d1383e847c3deb696ec7a2c9e33b9683772945660448a010b18063da67a4 android.hardware.gnss@1.1::IGnssMeasurement
-83e7a10ff3702147bd7ffa04567b20d407a3b16bbb7705644af44d919afe9103 android.hardware.gnss@1.1::IGnssMeasurementCallback
-0b96e0254e2168cfecb30c1ed5fb42681652cc00faa68c6e07568fafe64d1d50 android.hardware.graphics.common@1.1::types
-7d2cef99c838fb58038de8bbfd3cdb76ff4797241987077721715297f8d45e34 android.hardware.graphics.common@1.1::types # b/78135149
-d9b40a5b09962a5a0780b10fe33a4e607e69e2e088fc83de88a584115b7cb1c0 android.hardware.graphics.composer@2.2::IComposer
-a2f183f7fcc79aabedaef11095ab223aac0ed5ef984d850893872515e7f560c7 android.hardware.graphics.composer@2.2::IComposerClient
-dd83be076b6b3f10ed62ab34d8c8b95f2415961fb785200eb842e7bfb2b0ee92 android.hardware.graphics.mapper@2.1::IMapper
-675682dd3007805c985eaaec91612abc88f4c25b3431fb84070b7584a1a741fb android.hardware.health@2.0::IHealth
-434c4c32c00b0e54bb05e40c79503208b40f786a318029a2a4f66e34f10f2a76 android.hardware.health@2.0::IHealthInfoCallback
-c9e498f1ade5e26f00d290b4763a9671ec6720f915e7d592844b62e8cb1f9b5c android.hardware.health@2.0::types
-201f9723353fdbd40bf3705537fb7e015e4c399879425e68688fe0f43606ea4d android.hardware.keymaster@4.0::IKeymasterDevice
-1b7d2090c0a28b229d37c4b96160796b1f0d703950ac6ccc163fccd280830503 android.hardware.keymaster@4.0::types
-6d5c646a83538f0f9d8438c259932509f4353410c6c76e56db0d6ca98b69c3bb android.hardware.media.bufferpool@1.0::IAccessor
-b8c7ed58aa8740361e63d0ce9e7c94227572a629f356958840b34809d2393a7c android.hardware.media.bufferpool@1.0::IClientManager
-4a2c0dc82780e6c90731725a103feab8ab6ecf85a64e049b9cbd2b2c61620fe1 android.hardware.media.bufferpool@1.0::IConnection
-6aef1218e5949f867b0104752ac536c1b707222a403341720de90141df129e3e android.hardware.media.bufferpool@1.0::types
-7698dc2382a2eeb43541840e3ee624f34108efdfb976b2bfa7c13ef15fb8c4c4 android.hardware.neuralnetworks@1.1::IDevice
-72cc6126632456e8fbb8776fe50150c3c4dd5d09145653193affb70785211dfa android.hardware.neuralnetworks@1.1::types
-8d3d86da0bfa4bf070970d8303c659f67f35d670c287d45a3f542e4fedadd578 android.hardware.nfc@1.1::INfc
-e85f566698d2a2c28100e264fcf2c691a066756ddf8dd341d009ff50cfe10614 android.hardware.nfc@1.1::INfcClientCallback
-5e278fcaa3287d397d8eebe1c22aaa28150f5caae1cf9381cd6dc32cb37899c5 android.hardware.nfc@1.1::types
-163e115e833fc1d77cdd4a8cf0c833bb8b8d74fe35c880fe693101d17774926f android.hardware.power@1.2::IPower
-7899b9305587b2d5cd74a3cc87e9090f58bf4ae74256ce3ee36e7ec011822840 android.hardware.power@1.2::types
-5a464e6db53fad223986d655028a18185b73db8e2bfa9663f9042c9623eb0aa0 android.hardware.power@1.3::IPower
-a54a28d39b892d27a3cb06829181c038edcdd9e8eef359543b01e4313ae59aa0 android.hardware.power@1.3::types
-ab132c990a62f0aca35871c092c22fb9c85d478e22124ef6a4d0a2302da76a9f android.hardware.radio@1.2::IRadio
-cda752aeabaabc20486a82ac57a3dd107785c006094a349bc5e224e8aa22a17c android.hardware.radio@1.2::IRadioIndication
-da8c6ae991c6a4b284cc6e445332e064e28ee8a09482ed5afff9d159ec6694b7 android.hardware.radio@1.2::IRadioResponse
-b65332996eb39ba63300a1011404141fa59ce5c252bc17afae637be6eeca5f55 android.hardware.radio@1.2::ISap
-a9361522cc97ef66209d39ba324095b2f08344054bb4d3481e803eee0480623a android.hardware.radio@1.2::types
-87385469cf4409f0f33b01508e7a477cf71f2a11e466dd7e3ab5971a1baaa72b android.hardware.radio.config@1.0::IRadioConfig
-228b2ee3c8c276c9f0afad2dc313ca3d6bbd9e482ddf313c7204c60ad9b636ab android.hardware.radio.config@1.0::IRadioConfigIndication
-a2e9b7aa09f79426f765838174e04b6f9a3e6c8b76b923fc1705632207bad44b android.hardware.radio.config@1.0::IRadioConfigResponse
-4307696b64ded9bd8de06887f9dfc533e875c4e0d83b8008df4d705164bde0b1 android.hardware.radio.config@1.0::types
-bd7699f07ba5392310fefd33ea964e01f4f4a66015146845c85055004823cc81 android.hardware.secure_element@1.0::ISecureElement
-a65aa82bbe48d81a9ae9e86247bb1b89fd2d3138d4053d7a5b716c71149b7dee android.hardware.secure_element@1.0::ISecureElementHalCallback
-2984c069f48ba35cd1bf49b0e17daad0d418fef52cb7a4a84dba0043114063d4 android.hardware.secure_element@1.0::types
-b4f507b4dc9b5cd5f0e4445926acb7d94525ae60dc307b3951142283632207b6 android.hardware.soundtrigger@2.1::ISoundTriggerHw
-92c2cc0f06ef744c5bda21f1d660258f7937203109b493eee22c3f3e2dbb0d3e android.hardware.soundtrigger@2.1::ISoundTriggerHwCallback
-8ddfa7542772cc7bca19972b2d856264efa31914bfd098aeb7c2079d950194cb android.hardware.usb.gadget@1.0::IUsbGadget
-ad0a620cda08f01b151c30cb7afa23b0637cc84340cf8dec00ac8e32cf54a8db android.hardware.usb.gadget@1.0::IUsbGadgetCallback
-51fc20f223561ac3a32ace3217837ef3860265bd91c8b7ae3859532caef9bc39 android.hardware.usb.gadget@1.0::types
-1bfc9fd9536ed09f04bcaf222a332bc919f1565d4d08bddccdebe1bfca8f01b5 android.hardware.vibrator@1.2::IVibrator
-a0aefa29881235c21e4761d15c55edc35ef85c2e0d9e01d0966176d1dbf5f811 android.hardware.vibrator@1.2::types
-8bc75a0dfac15c6f87ffec950b76c7d7de30d516b54e8e0b1f3c0ff9c7c6873b android.hardware.wifi@1.2::IWifi
-780c16fdeda13b779d993953a67f7ca578c938a172a9424c1c715ae81bc40fd7 android.hardware.wifi@1.2::IWifiChip
-167af870fdb87e1cbbaa0fa62ef35e1031caad20dd1ba695983dedb1e9993486 android.hardware.wifi@1.2::IWifiChipEventCallback
-8c7ef32fc78d5ec6e6956de3784cc2c6f42614b5272d2e461f6d60534ba38ec2 android.hardware.wifi@1.2::IWifiNanIface
-1e6074efad9da333803fb7c1acdb719d51c30b2e1e92087b0420341631c30b60 android.hardware.wifi@1.2::IWifiNanIfaceEventCallback
-f5682dbf19f712bef9cc3faa5fe3dc670b6ffbcb62a147a1d86b9d43574cd83f android.hardware.wifi@1.2::IWifiStaIface
-6db2e7d274be2dca9bf3087afd1f774a68c99d2b4dc7eeaf41690e5cebcbef7a android.hardware.wifi@1.2::types
-ee08280de21cb41e3ec26d6ed636c701b7f70516e71fb22f4fe60a13e603f406 android.hardware.wifi.hostapd@1.0::IHostapd
-b2479cd7a417a1cf4f3a22db4e4579e21bac38fdcaf381e2bf10176d05397e01 android.hardware.wifi.hostapd@1.0::types
-e362203b941f18bd4cba29a62adfa02453ed00d6be5b72cdb6c4d7e0bf394a40 android.hardware.wifi.supplicant@1.1::ISupplicant
-21757d0e5dd4b7e4bd981a4a20531bca3c32271ad9777b17b74eb5a1ea508384 android.hardware.wifi.supplicant@1.1::ISupplicantStaIface
-cd4330c3196bda1d642a32abfe23a7d64ebfbda721940643af6867af3b3f0aa9 android.hardware.wifi.supplicant@1.1::ISupplicantStaIfaceCallback
-10ff2fae516346b86121368ce5790d5accdfcb73983246b813f3d488b66db45a android.hardware.wifi.supplicant@1.1::ISupplicantStaNetwork
diff --git a/prebuilt_hashes/Android.bp b/prebuilt_hashes/Android.bp
deleted file mode 100644
index 4692b76..0000000
--- a/prebuilt_hashes/Android.bp
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (C) 2018 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.
-
-python_binary_host {
-    name: "dump_hals_for_release",
-
-    srcs: [
-        "dump_hals_for_release.py",
-    ],
-
-    version: {
-        py2: {
-            enabled: true,
-        },
-        py3: {
-            enabled: false,
-        },
-    },
-}
diff --git a/prebuilt_hashes/dump_hals_for_release.py b/prebuilt_hashes/dump_hals_for_release.py
deleted file mode 100755
index e9ed4c2..0000000
--- a/prebuilt_hashes/dump_hals_for_release.py
+++ /dev/null
@@ -1,136 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (C) 2018 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.
-#
-
-"""
-Dump new HIDL types that are introduced in each dessert release.
-"""
-
-from __future__ import print_function
-
-import argparse
-import collections
-import json
-import os
-import re
-
-class Globals:
-    pass
-
-class Constants:
-    CURRENT = 'current'
-    HAL_PATH_PATTERN = r'/((?:[a-zA-Z_][a-zA-Z0-9_]*/)*)(\d+\.\d+)/([a-zA-Z_][a-zA-Z0-9_]*).hal'
-    CURRENT_TXT_PATTERN = r'(?:.*/)?([0-9]+|current).txt'
-
-def trim_trailing_comments(line):
-    idx = line.find('#')
-    if idx < 0: return line
-    return line[0:idx]
-
-def strip_begin(s, prefix):
-    if s.startswith(prefix):
-        return strip_begin(s[len(prefix):], prefix)
-    return s
-
-def strip_end(s, suffix):
-    if s.endswith(suffix):
-        return strip_end(s[0:-len(suffix)], suffix)
-    return s
-
-def get_interfaces(file_name):
-    with open(file_name) as file:
-        for line in file:
-            line_tokens = trim_trailing_comments(line).strip().split()
-            if not line_tokens:
-                continue
-            assert len(line_tokens) == 2, \
-                "Unrecognized line in file {}:\n{}".format(file_name, line)
-            yield line_tokens[1]
-
-def api_level_to_int(api_level):
-    try:
-        if api_level == Constants.CURRENT: return float('inf')
-        return int(api_level)
-    except ValueError:
-        return None
-
-def get_interfaces_from_package_root(package, root):
-    root = strip_end(root, '/')
-    for dirpath, _, filenames in os.walk(root, topdown=False):
-        dirpath = strip_begin(dirpath, root)
-        for filename in filenames:
-            filepath = os.path.join(dirpath, filename)
-            mo = re.match(Constants.HAL_PATH_PATTERN, filepath)
-            if not mo:
-                continue
-            yield '{}.{}@{}::{}'.format(
-                package, mo.group(1).strip('/').replace('/', '.'), mo.group(2), mo.group(3))
-
-def filter_out(iterable):
-    return iterable if not Globals.filter_out else filter(
-        lambda s: all(re.search(pattern, s) is None for pattern in Globals.filter_out),
-        iterable)
-
-def main():
-    parser = argparse.ArgumentParser(description=__doc__)
-    parser.add_argument('--pretty', help='Print pretty JSON', action='store_true')
-    parser.add_argument('--package-root', metavar='PACKAGE:PATH', nargs='*',
-        help='package root of current directory, e.g. android.hardware:hardware/interfaces')
-    parser.add_argument('--filter-out', metavar='REGEX', nargs='*',
-        help='A regular expression that filters out interfaces.')
-    parser.add_argument('hashes', metavar='FILE', nargs='*',
-        help='Locations of current.txt for each release.')
-    parser.parse_args(namespace=Globals)
-
-    interfaces_for_level = dict()
-
-    for filename in Globals.hashes or tuple():
-        mo = re.match(Constants.CURRENT_TXT_PATTERN, filename)
-        assert mo is not None, \
-            'Input hash file names must have the format {} but is {}'.format(Constants.CURRENT_TXT_PATTERN, filename)
-
-        api_level = mo.group(1)
-        assert api_level_to_int(api_level) is not None
-
-        if api_level not in interfaces_for_level:
-            interfaces_for_level[api_level] = set()
-        interfaces_for_level[api_level].update(filter_out(get_interfaces(filename)))
-
-    for package_root in Globals.package_root or tuple():
-        tup = package_root.split(':')
-        assert len(tup) == 2, \
-            '--package-root must have the format PACKAGE:PATH, but is {}'.format(package_root)
-        if Constants.CURRENT not in interfaces_for_level:
-            interfaces_for_level[Constants.CURRENT] = set()
-        interfaces_for_level[Constants.CURRENT].update(filter_out(get_interfaces_from_package_root(*tup)))
-
-    seen_interfaces = set()
-    new_interfaces_for_level = collections.OrderedDict()
-    for level, interfaces in sorted(interfaces_for_level.items(), key=lambda tup: api_level_to_int(tup[0])):
-        if level != Constants.CURRENT:
-            removed_interfaces = seen_interfaces - interfaces
-            assert not removed_interfaces, \
-                "The following interfaces are removed from API level {}:\n{}".format(
-                    level, removed_interfaces)
-        new_interfaces_for_level[level] = sorted(interfaces - seen_interfaces)
-        seen_interfaces.update(interfaces)
-
-    print(json.dumps(new_interfaces_for_level,
-        separators=None if Globals.pretty else (',',':'),
-        indent=4 if Globals.pretty else None))
-
-if __name__ == '__main__':
-    main()
diff --git a/radio/1.0/Android.bp b/radio/1.0/Android.bp
index 6765b4d..cd64bca 100644
--- a/radio/1.0/Android.bp
+++ b/radio/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
diff --git a/radio/1.0/IRadio.hal b/radio/1.0/IRadio.hal
index 236dbf5..66cd4f0 100644
--- a/radio/1.0/IRadio.hal
+++ b/radio/1.0/IRadio.hal
@@ -385,7 +385,10 @@
      *                  Note this is for backward compatibility with the old radio modem. The modem
      *                  must not use this param for any other reason.
      *
-     * Response function is IRadioResponse.setupDataCallResponse()
+     * Response function is IRadioResponse.setupDataCallResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher, use @1.2::IRadio.setupDataCall_1_2() instead.
      */
     oneway setupDataCall(int32_t serial, RadioTechnology radioTechnology,
             DataProfileInfo dataProfileInfo, bool modemCognitive, bool roamingAllowed,
@@ -546,7 +549,10 @@
      *        false => No specific reason specified
      *        true => Radio shutdown requested
      *
-     * Response function is IRadioResponse.deactivateDataCallResponse()
+     * Response function is IRadioResponse.deactivateDataCallResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher, use @1.2::IRadio.deactivateDataCall_1_2() instead.
      */
     oneway deactivateDataCall(int32_t serial, int32_t cid,
             bool reasonRadioShutDown);
@@ -1456,7 +1462,10 @@
      * @param reportInterval desired reporting interval (ms).
      * @param pullMode LCE service mode. true: PULL; false: PUSH.
      *
-     * Response callback is IRadioResponse.startLceServiceResponse()
+     * Response callback is IRadioResponse.startLceServiceResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher which use the always-on LCE that relies on indications.
      */
     oneway startLceService(int32_t serial, int32_t reportInterval, bool pullMode);
 
@@ -1466,7 +1475,10 @@
      *
      * @param serial Serial number of request.
      *
-     * Response callback is IRadioResponse.stopLceServiceResponse()
+     * Response callback is IRadioResponse.stopLceServiceResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher which use the always-on LCE that relies on indications.
      */
     oneway stopLceService(int32_t serial);
 
@@ -1475,7 +1487,10 @@
      *
      * @param serial Serial number of request.
      *
-     * Response callback is IRadioResponse.pullLceDataResponse()
+     * Response callback is IRadioResponse.pullLceDataResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher which use the always-on LCE that relies on indications.
      */
     oneway pullLceData(int32_t serial);
 
@@ -1546,7 +1561,10 @@
      * @param indicationFilter 32-bit bitmap of IndicationFilter. Bits set to 1 indicate the
      *        indications are enabled. See IndicationFilter for the definition of each bit.
      *
-     * Response callback is IRadioResponse.setIndicationFilterResponse()
+     * Response callback is IRadioResponse.setIndicationFilterResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher, use @1.2::IRadio.setIndicationFilter_1_2() instead.
      */
     oneway setIndicationFilter(int32_t serial, bitfield<IndicationFilter> indicationFilter);
 
@@ -1560,7 +1578,10 @@
      * @param serial Serial number of request
      * @param powerUp True if powering up the sim card
      *
-     * Response callback is IRadioResponse.setSimCardPowerResponse()
+     * Response callback is IRadioResponse.setSimCardPowerResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.1::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.1 or higher, use @1.1::IRadio.setSimCardPower_1_1() instead.
      */
     oneway setSimCardPower(int32_t serial, bool powerUp);
 
diff --git a/radio/1.0/IRadioIndication.hal b/radio/1.0/IRadioIndication.hal
index eb07226..eb6c8ef 100644
--- a/radio/1.0/IRadioIndication.hal
+++ b/radio/1.0/IRadioIndication.hal
@@ -427,6 +427,9 @@
     *
     * @param type Type of radio indication
     * @param lce LceData information
+    *
+    * DEPRECATED in @1.2 and above, use
+    * @1.2::IRadioIndication.currentLinkCapacityEstimate() instead.
     */
    oneway lceData(RadioIndicationType type, LceDataInfo lce);
 
diff --git a/radio/1.0/IRadioResponse.hal b/radio/1.0/IRadioResponse.hal
index c1b16b7..00cec9f 100644
--- a/radio/1.0/IRadioResponse.hal
+++ b/radio/1.0/IRadioResponse.hal
@@ -590,10 +590,10 @@
      *   RadioError:NONE must be returned on both success and failure of setup with the
      *              DataCallResponse.status containing the actual status
      *              For all other errors the DataCallResponse is ignored.
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OP_NOT_ALLOWED_BEFORE_REG_TO_NW
      *   RadioError:OP_NOT_ALLOWED_DURING_VOICE_CALL
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:INTERNAL_ERR
      *   RadioError:NO_MEMORY
@@ -856,12 +856,12 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:INVALID_CALL_ID
      *   RadioError:INVALID_STATE
      *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INTERNAL_ERR
      *   RadioError:NO_MEMORY
      *   RadioError:NO_RESOURCES
@@ -2420,11 +2420,11 @@
      * @param statusInfo LceStatusInfo indicating LCE status
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:LCE_NOT_SUPPORTED
      *   RadioError:INTERNAL_ERR
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:NO_MEMORY
      *   RadioError:NO_RESOURCES
      *   RadioError:CANCELLED
@@ -2436,6 +2436,7 @@
      * @param statusInfo LceStatusInfo indicating LCE status
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:LCE_NOT_SUPPORTED
@@ -2443,7 +2444,6 @@
      *   RadioError:NO_MEMORY
      *   RadioError:NO_RESOURCES
      *   RadioError:CANCELLED
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:SIM_ABSENT
      */
     oneway stopLceServiceResponse(RadioResponseInfo info, LceStatusInfo statusInfo);
@@ -2453,6 +2453,7 @@
      * @param lceInfo LceDataInfo indicating LCE data as defined in types.hal
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:LCE_NOT_SUPPORTED
@@ -2460,7 +2461,6 @@
      *   RadioError:NO_MEMORY
      *   RadioError:NO_RESOURCES
      *   RadioError:CANCELLED
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:SIM_ABSENT
      */
     oneway pullLceDataResponse(RadioResponseInfo info, LceDataInfo lceInfo);
@@ -2534,13 +2534,13 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:NO_MEMORY
      *   RadioError:INTERNAL_ERR
      *   RadioError:SYSTEM_ERR
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:NO_RESOURCES
      *   RadioError:CANCELLED
      */
@@ -2550,9 +2550,9 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.1 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
-     *   RadioError:REQUEST_NOT_SUPPORTED
      *   RadioError:INVALID_ARGUMENTS
      *   RadioError:INTERNAL_ERR
      *   RadioError:NO_MEMORY
diff --git a/radio/1.0/types.hal b/radio/1.0/types.hal
index 8393cf5..cafa5a6 100644
--- a/radio/1.0/types.hal
+++ b/radio/1.0/types.hal
@@ -166,11 +166,15 @@
 };
 
 enum CardState : int32_t {
+    /* card is physically absent from device. (Some old modems use CardState.ABSENT when the SIM
+       is powered off. This is no longer correct, however the platform will still support this
+       legacy behavior.) */
     ABSENT,
+    /* card is inserted in the device */
     PRESENT,
     ERROR,
-    RESTRICTED,                           // card is present but not usable due to carrier
-                                          // restrictions
+    /* card is present but not usable due to carrier restrictions */
+    RESTRICTED,
 };
 
 enum PinState : int32_t {
@@ -1890,8 +1894,8 @@
 };
 
 struct CarrierRestrictions {
-    vec<Carrier> allowedCarriers;         // whitelist for allowed carriers
-    vec<Carrier> excludedCarriers;        // blacklist for explicitly excluded carriers
+    vec<Carrier> allowedCarriers;         // Allowed carriers
+    vec<Carrier> excludedCarriers;        // Explicitly excluded carriers
                                           // which match allowed_carriers. Eg. allowedCarriers
                                           // match mcc/mnc, excludedCarriers has same mcc/mnc and
                                           // gid1 is ABCD. It means except the carrier whose gid1
diff --git a/radio/1.0/vts/OWNERS b/radio/1.0/vts/OWNERS
index 2384317..9310f8e 100644
--- a/radio/1.0/vts/OWNERS
+++ b/radio/1.0/vts/OWNERS
@@ -1,8 +1,7 @@
 # Telephony team
 amitmahajan@google.com
-sanketpadawe@google.com
 shuoq@google.com
+jackyu@google.com
 
 # VTS team
-yuexima@google.com
-yim@google.com
+dshi@google.com
diff --git a/radio/1.0/vts/functional/Android.bp b/radio/1.0/vts/functional/Android.bp
index 13fc542..2c0e70a 100644
--- a/radio/1.0/vts/functional/Android.bp
+++ b/radio/1.0/vts/functional/Android.bp
@@ -14,49 +14,70 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["radio_hidl_hal_cell_broadcast.cpp",
-           "radio_hidl_hal_data.cpp",
-           "radio_hidl_hal_icc.cpp",
-           "radio_hidl_hal_ims.cpp",
-           "radio_hidl_hal_misc.cpp",
-           "radio_hidl_hal_sms.cpp",
-           "radio_hidl_hal_stk.cpp",
-           "radio_hidl_hal_test.cpp",
-           "radio_hidl_hal_voice.cpp",
-           "radio_indication.cpp",
-           "radio_response.cpp",
-           "VtsHalRadioV1_0TargetTest.cpp",
-           "vts_test_util.cpp"],
+    srcs: [
+        "radio_hidl_hal_cell_broadcast.cpp",
+        "radio_hidl_hal_data.cpp",
+        "radio_hidl_hal_icc.cpp",
+        "radio_hidl_hal_ims.cpp",
+        "radio_hidl_hal_misc.cpp",
+        "radio_hidl_hal_sms.cpp",
+        "radio_hidl_hal_stk.cpp",
+        "radio_hidl_hal_test.cpp",
+        "radio_hidl_hal_voice.cpp",
+        "radio_indication.cpp",
+        "radio_response.cpp",
+        "VtsHalRadioV1_0TargetTest.cpp",
+        "vts_test_util.cpp",
+    ],
     static_libs: [
         "android.hardware.radio@1.0",
+        "android.hardware.radio@1.1",
+        "android.hardware.radio@1.2",
     ],
     test_config: "vts_hal_radio_target_test.xml",
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
 
 cc_test {
     name: "VtsHalSapV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["sap_callback.cpp",
-           "sap_hidl_hal_api.cpp",
-           "sap_hidl_hal_test.cpp",
-           "VtsHalSapV1_0TargetTest.cpp",
-           "vts_test_util.cpp"],
+    srcs: [
+        "sap_callback.cpp",
+        "sap_hidl_hal_api.cpp",
+        "sap_hidl_hal_test.cpp",
+        "VtsHalSapV1_0TargetTest.cpp",
+        "vts_test_util.cpp",
+    ],
     static_libs: [
         "android.hardware.radio@1.0",
     ],
     test_config: "vts_hal_sap_target_test.xml",
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
 
 cc_library_static {
     name: "RadioVtsTestUtilBase",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs : [
-        "vts_test_util.cpp"
+    srcs: [
+        "vts_test_util.cpp",
     ],
     shared_libs: [
         "android.hardware.radio@1.0",
diff --git a/radio/1.0/vts/functional/VtsHalRadioV1_0TargetTest.cpp b/radio/1.0/vts/functional/VtsHalRadioV1_0TargetTest.cpp
index 9d61b52..2d0fe4b 100644
--- a/radio/1.0/vts/functional/VtsHalRadioV1_0TargetTest.cpp
+++ b/radio/1.0/vts/functional/VtsHalRadioV1_0TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <radio_hidl_hal_utils_v1_0.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_0::IRadio::descriptor)),
diff --git a/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
index b80b971..c7ffa90 100644
--- a/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
+++ b/radio/1.0/vts/functional/VtsHalSapV1_0TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <sap_hidl_hal_utils.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SapHidlTest);
 INSTANTIATE_TEST_SUITE_P(PerInstance, SapHidlTest,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_0::ISap::descriptor)),
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_data.cpp b/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
index e3ee9d4..655b869 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_data.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/logging.h>
+#include <android/hardware/radio/1.2/IRadio.h>
 #include <radio_hidl_hal_utils_v1_0.h>
 
 using namespace ::android::hardware::radio::V1_0;
@@ -139,6 +140,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // setupDataCall is deprecated on radio::V1_2 with setupDataCall_1_2
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp->rspInfo.error,
                                      {RadioError::NONE, RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW,
@@ -164,6 +168,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // deactivateDataCall is deprecated on radio::V1_2 with deactiveDataCall_1_2
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp->rspInfo.error,
                                      {RadioError::NONE, RadioError::RADIO_NOT_AVAILABLE,
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp b/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
index 3f96473..624d003 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
+++ b/radio/1.0/vts/functional/radio_hidl_hal_misc.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <android-base/logging.h>
+#include <android/hardware/radio/1.2/IRadio.h>
 #include <radio_hidl_hal_utils_v1_0.h>
 
 /*
@@ -771,6 +772,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // HAL 1.2 and later use the always-on LCE that relies on indications.
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(
             radioRsp->rspInfo.error,
@@ -792,6 +796,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // HAL 1.2 and later use the always-on LCE that relies on indications.
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp->rspInfo.error,
                                      {RadioError::NONE, RadioError::LCE_NOT_SUPPORTED,
@@ -812,6 +819,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // HAL 1.2 and later use the always-on LCE that relies on indications.
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp->rspInfo.error,
                                      {RadioError::NONE, RadioError::INTERNAL_ERR,
@@ -971,6 +981,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // setIndicationFilter is deprecated on radio::V1_2 with setIndicationFilter_1_2
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     std::cout << static_cast<int>(radioRsp->rspInfo.error) << std::endl;
 
     if (cardStatus.cardState == CardState::ABSENT) {
@@ -992,6 +1005,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp->rspInfo.type);
     EXPECT_EQ(serial, radioRsp->rspInfo.serial);
 
+    // setSimCardPower is deprecated on radio::V1_1 with setSimCardPower_1_1
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_1);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp->rspInfo.error,
                                      {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
diff --git a/radio/1.0/vts/functional/radio_hidl_hal_utils_v1_0.h b/radio/1.0/vts/functional/radio_hidl_hal_utils_v1_0.h
index 8a551f7..e3e9473 100644
--- a/radio/1.0/vts/functional/radio_hidl_hal_utils_v1_0.h
+++ b/radio/1.0/vts/functional/radio_hidl_hal_utils_v1_0.h
@@ -38,6 +38,8 @@
 
 #define TIMEOUT_PERIOD 75
 #define RADIO_SERVICE_NAME "slot1"
+#define SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(__ver__) \
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL(__ver__, radio, radioRsp)
 
 class RadioHidlTest;
 extern CardStatus cardStatus;
diff --git a/radio/1.0/vts/functional/vts_test_util.cpp b/radio/1.0/vts/functional/vts_test_util.cpp
index 9a2d089..5b31acc 100644
--- a/radio/1.0/vts/functional/vts_test_util.cpp
+++ b/radio/1.0/vts/functional/vts_test_util.cpp
@@ -19,6 +19,8 @@
 #include <iostream>
 #include "VtsCoreUtil.h"
 
+#define WAIT_TIMEOUT_PERIOD 75
+
 int GetRandomSerialNumber() {
     return rand();
 }
@@ -81,6 +83,13 @@
     return hasFeature;
 }
 
+bool isSsSsEnabled() {
+    // Do not use checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "")
+    // until b/148904287 is fixed. We need exact matching instead of partial matching. (i.e.
+    // by definition the empty string "" is a substring of any string).
+    return !isDsDsEnabled() && !isTsTsEnabled();
+}
+
 bool isDsDsEnabled() {
     return testing::checkSubstringInCommandOutput("getprop persist.radio.multisim.config", "dsds");
 }
@@ -99,4 +108,33 @@
            ::android::hardware::radio::V1_0::RegState::NOT_REG_MT_SEARCHING_OP_EM == state ||
            ::android::hardware::radio::V1_0::RegState::REG_DENIED_EM == state ||
            ::android::hardware::radio::V1_0::RegState::UNKNOWN_EM == state;
-}
\ No newline at end of file
+}
+
+/*
+ * Notify that the response message is received.
+ */
+void RadioResponseWaiter::notify(int receivedSerial) {
+    std::unique_lock<std::mutex> lock(mtx_);
+    if (serial == receivedSerial) {
+        count_++;
+        cv_.notify_one();
+    }
+}
+
+/*
+ * Wait till the response message is notified or till WAIT_TIMEOUT_PERIOD.
+ */
+std::cv_status RadioResponseWaiter::wait() {
+    std::unique_lock<std::mutex> lock(mtx_);
+
+    std::cv_status status = std::cv_status::no_timeout;
+    auto now = std::chrono::system_clock::now();
+    while (count_ == 0) {
+        status = cv_.wait_until(lock, now + std::chrono::seconds(WAIT_TIMEOUT_PERIOD));
+        if (status == std::cv_status::timeout) {
+            return status;
+        }
+    }
+    count_--;
+    return status;
+}
diff --git a/radio/1.0/vts/functional/vts_test_util.h b/radio/1.0/vts/functional/vts_test_util.h
index 1625f11..fa338a3 100644
--- a/radio/1.0/vts/functional/vts_test_util.h
+++ b/radio/1.0/vts/functional/vts_test_util.h
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#pragma once
+
 #include <android-base/logging.h>
 
 #include <android/hardware/radio/1.0/types.h>
@@ -25,6 +27,20 @@
 using ::android::hardware::radio::V1_0::SapResultCode;
 using namespace std;
 
+/*
+ * MACRO used to skip test case when radio response return error REQUEST_NOT_SUPPORTED
+ * on HAL versions which has deprecated the request interfaces. The MACRO can only be used
+ * AFTER receiving radio response.
+ */
+#define SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL(__ver__, __radio__, __radioRsp__)      \
+    do {                                                                                   \
+        sp<::android::hardware::radio::V##__ver__::IRadio> __radio =                       \
+                ::android::hardware::radio::V##__ver__::IRadio::castFrom(__radio__);       \
+        if (__radio && __radioRsp__->rspInfo.error == RadioError::REQUEST_NOT_SUPPORTED) { \
+            GTEST_SKIP() << "REQUEST_NOT_SUPPORTED";                                       \
+        }                                                                                  \
+    } while (0)
+
 enum CheckFlag {
     CHECK_DEFAULT = 0,
     CHECK_GENERAL_ERROR = 1,
@@ -35,6 +51,12 @@
 
 static constexpr const char* FEATURE_VOICE_CALL = "android.software.connectionservice";
 
+static constexpr const char* FEATURE_TELEPHONY = "android.hardware.telephony";
+
+static constexpr const char* FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
+
+static constexpr const char* FEATURE_TELEPHONY_CDMA = "android.hardware.telephony.cdma";
+
 /*
  * Generate random serial number for radio test
  */
@@ -58,12 +80,17 @@
 bool deviceSupportsFeature(const char* feature);
 
 /*
- * Check if device is in DSDS.
+ * Check if device is in SsSs (Single SIM Single Standby).
+ */
+bool isSsSsEnabled();
+
+/*
+ * Check if device is in DSDS (Dual SIM Dual Standby).
  */
 bool isDsDsEnabled();
 
 /*
- * Check if device is in TSTS.
+ * Check if device is in TSTS (Triple SIM Triple Standby).
  */
 bool isTsTsEnabled();
 
@@ -75,4 +102,24 @@
 /*
  * Check if voice status is in service.
  */
-bool isVoiceInService(RegState state);
\ No newline at end of file
+bool isVoiceInService(RegState state);
+
+/**
+ * Used when waiting for an asynchronous response from the HAL.
+ */
+class RadioResponseWaiter {
+  protected:
+    std::mutex mtx_;
+    std::condition_variable cv_;
+    int count_;
+
+  public:
+    /* Serial number for radio request */
+    int serial;
+
+    /* Used as a mechanism to inform the test about data/event callback */
+    void notify(int receivedSerial);
+
+    /* Test code calls this function to wait for response */
+    std::cv_status wait();
+};
diff --git a/radio/1.1/Android.bp b/radio/1.1/Android.bp
index 28388b0..abff05c 100644
--- a/radio/1.1/Android.bp
+++ b/radio/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
diff --git a/radio/1.1/IRadio.hal b/radio/1.1/IRadio.hal
index 22d27d4..de2135a 100644
--- a/radio/1.1/IRadio.hal
+++ b/radio/1.1/IRadio.hal
@@ -77,7 +77,10 @@
      * @param serial Serial number of request.
      * @param request Defines the radio networks/bands/channels which need to be scanned.
      *
-     * Response function is IRadioResponse.startNetworkScanResponse()
+     * Response function is IRadioResponse.startNetworkScanResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.2::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.2 or higher, use @1.2::IRadio.startNetworkScan_1_2() instead.
      */
     oneway startNetworkScan(int32_t serial, NetworkScanRequest request);
 
diff --git a/radio/1.1/IRadioResponse.hal b/radio/1.1/IRadioResponse.hal
index 759602b..7dc1bc6 100644
--- a/radio/1.1/IRadioResponse.hal
+++ b/radio/1.1/IRadioResponse.hal
@@ -51,6 +51,7 @@
      * @param info Response info struct containing response type, serial no. and error
      *
      * Valid errors returned:
+     *   RadioError:REQUEST_NOT_SUPPORTED may be returned when HAL 1.2 or higher is supported.
      *   RadioError:NONE
      *   RadioError:RADIO_NOT_AVAILABLE
      *   RadioError:OPERATION_NOT_ALLOWED
@@ -59,7 +60,6 @@
      *   RadioError:NO_MEMORY
      *   RadioError:MODEM_ERR
      *   RadioError:INVALID_ARGUMENTS
-     *   RadioError:REQUEST_NOT_SUPPORTED
      */
     oneway startNetworkScanResponse(RadioResponseInfo info);
 
diff --git a/radio/1.1/vts/OWNERS b/radio/1.1/vts/OWNERS
index 2384317..a07c917 100644
--- a/radio/1.1/vts/OWNERS
+++ b/radio/1.1/vts/OWNERS
@@ -1,8 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.1/vts/functional/Android.bp b/radio/1.1/vts/functional/Android.bp
index e1278b9..b3def8e 100644
--- a/radio/1.1/vts/functional/Android.bp
+++ b/radio/1.1/vts/functional/Android.bp
@@ -14,21 +14,36 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["radio_hidl_hal_api.cpp",
-           "radio_hidl_hal_test.cpp",
-           "radio_indication.cpp",
-           "radio_response.cpp",
-           "VtsHalRadioV1_1TargetTest.cpp"],
+    srcs: [
+        "radio_hidl_hal_api.cpp",
+        "radio_hidl_hal_test.cpp",
+        "radio_indication.cpp",
+        "radio_response.cpp",
+        "VtsHalRadioV1_1TargetTest.cpp",
+    ],
     static_libs: [
         "RadioVtsTestUtilBase",
+        "android.hardware.radio@1.2",
         "android.hardware.radio@1.1",
         "android.hardware.radio@1.0",
     ],
     header_libs: [
         "radio.util.header@1.0",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/1.1/vts/functional/VtsHalRadioV1_1TargetTest.cpp b/radio/1.1/vts/functional/VtsHalRadioV1_1TargetTest.cpp
index 98dbf62..53d4d21 100644
--- a/radio/1.1/vts/functional/VtsHalRadioV1_1TargetTest.cpp
+++ b/radio/1.1/vts/functional/VtsHalRadioV1_1TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <radio_hidl_hal_utils_v1_1.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_1);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_1,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_1::IRadio::descriptor)),
diff --git a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
index 08121fd..389944b 100644
--- a/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.1/vts/functional/radio_hidl_hal_api.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/hardware/radio/1.2/IRadio.h>
 #include <radio_hidl_hal_utils_v1_1.h>
 #include <vector>
 
@@ -107,6 +108,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_1->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_1->rspInfo.serial);
 
+    // startNetworkScan is deprecated on radio::V1_2 with startNetworkScan_1_2
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ALOGI("startNetworkScan, rspInfo.error = %d\n", (int32_t)radioRsp_v1_1->rspInfo.error);
         ASSERT_TRUE(CheckAnyOfErrors(
@@ -131,6 +135,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_1->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_1->rspInfo.serial);
 
+    // startNetworkScan is deprecated on radio::V1_2 with startNetworkScan_1_2
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_2);
+
     if (cardStatus.cardState == CardState::ABSENT) {
         ALOGI("startNetworkScan_InvalidArgument, rspInfo.error = %d\n",
               (int32_t)radioRsp_v1_1->rspInfo.error);
diff --git a/radio/1.1/vts/functional/radio_hidl_hal_utils_v1_1.h b/radio/1.1/vts/functional/radio_hidl_hal_utils_v1_1.h
index b81ee13..bafde77 100644
--- a/radio/1.1/vts/functional/radio_hidl_hal_utils_v1_1.h
+++ b/radio/1.1/vts/functional/radio_hidl_hal_utils_v1_1.h
@@ -40,6 +40,8 @@
 
 #define TIMEOUT_PERIOD 75
 #define RADIO_SERVICE_NAME "slot1"
+#define SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(__ver__) \
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL(__ver__, radio_v1_1, radioRsp_v1_1)
 
 class RadioHidlTest_v1_1;
 extern CardStatus cardStatus;
diff --git a/radio/1.2/Android.bp b/radio/1.2/Android.bp
index 28e6b26..924c77a 100644
--- a/radio/1.2/Android.bp
+++ b/radio/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
diff --git a/radio/1.2/IRadio.hal b/radio/1.2/IRadio.hal
index 87b0add..6d48ca0 100644
--- a/radio/1.2/IRadio.hal
+++ b/radio/1.2/IRadio.hal
@@ -37,7 +37,10 @@
      * @param serial Serial number of request.
      * @param request Defines the radio networks/bands/channels which need to be scanned.
      *
-     * Response function is IRadioResponse.startNetworkScanResponse()
+     * Response function is IRadioResponse.startNetworkScanResponse() which may return
+     * RadioError:REQUEST_NOT_SUPPORTED if @1.4::IRadio or higher is supported.
+     *
+     * DEPRECATED in @1.4 or higher, use @1.4::IRadio.startNetworkScan_1_4() instead.
      */
     oneway startNetworkScan_1_2(int32_t serial, NetworkScanRequest request);
 
diff --git a/radio/1.2/default/Android.bp b/radio/1.2/default/Android.bp
index 74fcf11..7b3dd1f 100644
--- a/radio/1.2/default/Android.bp
+++ b/radio/1.2/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.radio@1.2-radio-service",
     init_rc: ["android.hardware.radio@1.2-radio-service.rc"],
diff --git a/radio/1.2/default/Radio.cpp b/radio/1.2/default/Radio.cpp
index 73512e4..28a815f 100644
--- a/radio/1.2/default/Radio.cpp
+++ b/radio/1.2/default/Radio.cpp
@@ -52,16 +52,16 @@
     /**
      * IRadio-defined request is called from the client and talk to the radio to get
      * IRadioResponse-defined response or/and IRadioIndication-defined indication back to the
-     * client. This dummy implementation omits and replaces the design and implementation of vendor
+     * client. This implementation omits and replaces the design and implementation of vendor
      * codes that needs to handle the receipt of the request and the return of the response from the
-     * radio; this just directly returns a dummy response back to the client.
+     * radio; this just directly returns a fake response back to the client.
      */
 
     ALOGD("Radio Request: getIccCardStatus is entering");
 
     if (mRadioResponse != nullptr || mRadioResponseV1_1 != nullptr ||
         mRadioResponseV1_2 != nullptr) {
-        // Dummy RadioResponseInfo as part of response to return in 1.0, 1.1 and 1.2
+        // Fake RadioResponseInfo as part of response to return in 1.0, 1.1 and 1.2
         ::android::hardware::radio::V1_0::RadioResponseInfo info;
         info.serial = serial;
         info.type = ::android::hardware::radio::V1_0::RadioResponseType::SOLICITED;
@@ -72,7 +72,7 @@
          * return getIccCardStatusResponse.
          */
         if (mRadioResponseV1_2 != nullptr) {
-            // Dummy CardStatus as part of getIccCardStatusResponse_1_2 response to return
+            // Fake CardStatus as part of getIccCardStatusResponse_1_2 response to return
             ::android::hardware::radio::V1_2::CardStatus card_status;
             card_status.base.cardState = ::android::hardware::radio::V1_0::CardState::ABSENT;
             card_status.base.gsmUmtsSubscriptionAppIndex = 0;
@@ -80,7 +80,7 @@
             mRadioResponseV1_2->getIccCardStatusResponse_1_2(info, card_status);
             ALOGD("Radio Response: getIccCardStatusResponse_1_2 is sent");
         } else if (mRadioResponseV1_1 != nullptr) {
-            // Dummy CardStatus as part of getIccCardStatusResponse response to return
+            // Fake CardStatus as part of getIccCardStatusResponse response to return
             ::android::hardware::radio::V1_0::CardStatus card_status_V1_0;
             card_status_V1_0.cardState = ::android::hardware::radio::V1_0::CardState::ABSENT;
             card_status_V1_0.gsmUmtsSubscriptionAppIndex = 0;
@@ -88,7 +88,7 @@
             mRadioResponseV1_1->getIccCardStatusResponse(info, card_status_V1_0);
             ALOGD("Radio Response: getIccCardStatusResponse is sent");
         } else {
-            // Dummy CardStatus as part of getIccCardStatusResponse response to return
+            // Fake CardStatus as part of getIccCardStatusResponse response to return
             ::android::hardware::radio::V1_0::CardStatus card_status_V1_0;
             card_status_V1_0.cardState = ::android::hardware::radio::V1_0::CardState::ABSENT;
             card_status_V1_0.gsmUmtsSubscriptionAppIndex = 0;
diff --git a/radio/1.2/vts/OWNERS b/radio/1.2/vts/OWNERS
index 245d9d4..a07c917 100644
--- a/radio/1.2/vts/OWNERS
+++ b/radio/1.2/vts/OWNERS
@@ -1,9 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-sasindran@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.2/vts/functional/Android.bp b/radio/1.2/vts/functional/Android.bp
index 56f2d5f..a62000f 100644
--- a/radio/1.2/vts/functional/Android.bp
+++ b/radio/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -27,6 +36,8 @@
     ],
     static_libs: [
         "RadioVtsTestUtilBase",
+        "android.hardware.radio@1.4",
+        "android.hardware.radio@1.3",
         "android.hardware.radio@1.2",
         "android.hardware.radio@1.1",
         "android.hardware.radio@1.0",
@@ -34,5 +45,8 @@
         "android.hardware.radio.config@1.1",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/1.2/vts/functional/VtsHalRadioV1_2TargetTest.cpp b/radio/1.2/vts/functional/VtsHalRadioV1_2TargetTest.cpp
index 400e394..1840c3e 100644
--- a/radio/1.2/vts/functional/VtsHalRadioV1_2TargetTest.cpp
+++ b/radio/1.2/vts/functional/VtsHalRadioV1_2TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <radio_hidl_hal_utils_v1_2.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_2);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_2,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_2::IRadio::descriptor)),
diff --git a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
index acb1b0e..2400bde 100644
--- a/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.2/vts/functional/radio_hidl_hal_api.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#include <android/hardware/radio/1.4/IRadio.h>
 #include <radio_hidl_hal_utils_v1_2.h>
 #include <vector>
 
@@ -57,6 +58,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan, rspInfo.error = %s\n", toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
         ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_2->rspInfo.error, {RadioError::SIM_ABSENT}));
@@ -94,6 +98,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidArgument, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -126,6 +133,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidInterval1, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -158,6 +168,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidInterval2, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -190,6 +203,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidMaxSearchTime1, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -222,6 +238,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidMaxSearchTime2, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -254,6 +273,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidPeriodicity1, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -286,6 +308,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidPeriodicity2, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -322,6 +347,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidArgument, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
@@ -359,6 +387,9 @@
     EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_2->rspInfo.type);
     EXPECT_EQ(serial, radioRsp_v1_2->rspInfo.serial);
 
+    // startNetworkScan_1_2 is deprecated in radio::V1_4 with startNetworkScan_1_4
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(1_4);
+
     ALOGI("startNetworkScan_InvalidArgument, rspInfo.error = %s\n",
           toString(radioRsp_v1_2->rspInfo.error).c_str());
     if (cardStatus.base.cardState == CardState::ABSENT) {
diff --git a/radio/1.2/vts/functional/radio_hidl_hal_utils_v1_2.h b/radio/1.2/vts/functional/radio_hidl_hal_utils_v1_2.h
index 479340c..81286d2 100644
--- a/radio/1.2/vts/functional/radio_hidl_hal_utils_v1_2.h
+++ b/radio/1.2/vts/functional/radio_hidl_hal_utils_v1_2.h
@@ -50,6 +50,8 @@
 
 #define TIMEOUT_PERIOD 75
 #define RADIO_SERVICE_NAME "slot1"
+#define SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL_VERSION_AT_LEAST(__ver__) \
+    SKIP_TEST_IF_REQUEST_NOT_SUPPORTED_WITH_HAL(__ver__, radio_v1_2, radioRsp_v1_2)
 
 class RadioHidlTest_v1_2;
 extern ::android::hardware::radio::V1_2::CardStatus cardStatus;
@@ -682,4 +684,4 @@
 
     /* radio config service handle */
     sp<IRadioConfig> radioConfig;
-};
\ No newline at end of file
+};
diff --git a/radio/1.3/Android.bp b/radio/1.3/Android.bp
index b6af874..8d24b2b 100644
--- a/radio/1.3/Android.bp
+++ b/radio/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
diff --git a/radio/1.3/vts/OWNERS b/radio/1.3/vts/OWNERS
index d642064..a07c917 100644
--- a/radio/1.3/vts/OWNERS
+++ b/radio/1.3/vts/OWNERS
@@ -1,10 +1 @@
-# Telephony team
-amitmahajan@google.com
-sanketpadawe@google.com
-shuoq@google.com
-sasindran@google.com
-nazaninb@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.3/vts/functional/Android.bp b/radio/1.3/vts/functional/Android.bp
index e32258f..d96d391 100644
--- a/radio/1.3/vts/functional/Android.bp
+++ b/radio/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -32,5 +41,8 @@
         "android.hardware.radio@1.0",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/1.3/vts/functional/VtsHalRadioV1_3TargetTest.cpp b/radio/1.3/vts/functional/VtsHalRadioV1_3TargetTest.cpp
index 2622bbc..e6fd9f9 100644
--- a/radio/1.3/vts/functional/VtsHalRadioV1_3TargetTest.cpp
+++ b/radio/1.3/vts/functional/VtsHalRadioV1_3TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <radio_hidl_hal_utils_v1_3.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_3);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_3,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_3::IRadio::descriptor)),
diff --git a/radio/1.4/Android.bp b/radio/1.4/Android.bp
index ff2e0d6..385f2d3 100644
--- a/radio/1.4/Android.bp
+++ b/radio/1.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
diff --git a/radio/1.4/types.hal b/radio/1.4/types.hal
index 393716b..a830816 100644
--- a/radio/1.4/types.hal
+++ b/radio/1.4/types.hal
@@ -1847,9 +1847,9 @@
     /**
      * SS reference signal received quality, multipled by -1.
      *
-     * Reference: 3GPP TS 38.215.
+     * Reference: 3GPP TS 38.215, 3GPP TS 38.133 section 10.
      *
-     * Range [3, 20], INT_MAX means invalid/unreported.
+     * Range [-20 dB, 43 dB], INT_MAX means invalid/unreported.
      */
     int32_t ssRsrq;
 
diff --git a/radio/1.4/vts/OWNERS b/radio/1.4/vts/OWNERS
index fd69f36..a07c917 100644
--- a/radio/1.4/vts/OWNERS
+++ b/radio/1.4/vts/OWNERS
@@ -1,8 +1 @@
-# Telephony team
-amitmahajan@google.com
-shuoq@google.com
-sasindran@google.com
-
-# VTS team
-yuexima@google.com
-yim@google.com
\ No newline at end of file
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.4/vts/functional/Android.bp b/radio/1.4/vts/functional/Android.bp
index 369b55b..167dec8 100644
--- a/radio/1.4/vts/functional/Android.bp
+++ b/radio/1.4/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_4TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -35,5 +44,8 @@
         "android.hardware.radio.config@1.1",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"]
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/1.4/vts/functional/AndroidTest.xml b/radio/1.4/vts/functional/AndroidTest.xml
index 9df8f9c..469e103 100644
--- a/radio/1.4/vts/functional/AndroidTest.xml
+++ b/radio/1.4/vts/functional/AndroidTest.xml
@@ -13,7 +13,7 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
-<configuration description="Runs VtsHalRadioV1_3TargetTest.">
+<configuration description="Runs VtsHalRadioV1_4TargetTest.">
     <option name="test-suite-tag" value="apct" />
     <option name="test-suite-tag" value="apct-native" />
 
@@ -24,11 +24,11 @@
 
     <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
         <option name="cleanup" value="true" />
-        <option name="push" value="VtsHalRadioV1_3TargetTest->/data/local/tmp/VtsHalRadioV1_3TargetTest" />
+        <option name="push" value="VtsHalRadioV1_4TargetTest->/data/local/tmp/VtsHalRadioV1_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="VtsHalRadioV1_3TargetTest" />
+        <option name="module-name" value="VtsHalRadioV1_4TargetTest" />
     </test>
 </configuration>
diff --git a/radio/1.4/vts/functional/VtsHalRadioV1_4TargetTest.cpp b/radio/1.4/vts/functional/VtsHalRadioV1_4TargetTest.cpp
index 23ec011..eadd6c7 100644
--- a/radio/1.4/vts/functional/VtsHalRadioV1_4TargetTest.cpp
+++ b/radio/1.4/vts/functional/VtsHalRadioV1_4TargetTest.cpp
@@ -19,6 +19,7 @@
 #include <hidl/ServiceManagement.h>
 #include <radio_hidl_hal_utils_v1_4.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_4);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_4,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  ::android::hardware::radio::V1_4::IRadio::descriptor)),
diff --git a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
index 1b254a1..b0b984c 100644
--- a/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.4/vts/functional/radio_hidl_hal_api.cpp
@@ -34,6 +34,10 @@
     if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
         ALOGI("Skipping emergencyDial because voice call is not supported in device");
         return;
+    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+        return;
     } else {
         ALOGI("Running emergencyDial because voice call is supported in device");
     }
@@ -86,6 +90,10 @@
     if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
         ALOGI("Skipping emergencyDial because voice call is not supported in device");
         return;
+    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+        return;
     } else {
         ALOGI("Running emergencyDial because voice call is supported in device");
     }
@@ -138,6 +146,10 @@
     if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
         ALOGI("Skipping emergencyDial because voice call is not supported in device");
         return;
+    } else if (!deviceSupportsFeature(FEATURE_TELEPHONY_GSM) &&
+               !deviceSupportsFeature(FEATURE_TELEPHONY_CDMA)) {
+        ALOGI("Skipping emergencyDial because gsm/cdma radio is not supported in device");
+        return;
     } else {
         ALOGI("Running emergencyDial because voice call is supported in device");
     }
diff --git a/tests/bar/1.0/.hidl_for_test b/radio/1.5/.hidl_for_system_ext
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to radio/1.5/.hidl_for_system_ext
diff --git a/radio/1.5/Android.bp b/radio/1.5/Android.bp
index 06a2a6e..a36f296 100644
--- a/radio/1.5/Android.bp
+++ b/radio/1.5/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio@1.5",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadio.hal",
@@ -22,4 +28,5 @@
         "android.hidl.safe_union@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/radio/1.5/types.hal b/radio/1.5/types.hal
index b061bd5..c1f3f03 100644
--- a/radio/1.5/types.hal
+++ b/radio/1.5/types.hal
@@ -107,9 +107,9 @@
     SSRSRP = 6,
     /**
      * 5G SS reference signal received quality.
-     * Range: -20 dB to -3 dB.
+     * Range: -43 dB to 20 dB.
      * Used RAN: NGRAN
-     * Reference: 3GPP TS 38.215.
+     * Reference: 3GPP TS 38.215, 3GPP TS 38.133 section 10
      */
     SSRSRQ = 7,
     /**
diff --git a/radio/1.5/vts/OWNERS b/radio/1.5/vts/OWNERS
index 3629a6c..a07c917 100644
--- a/radio/1.5/vts/OWNERS
+++ b/radio/1.5/vts/OWNERS
@@ -1,10 +1 @@
-# Telephony team
-refuhoo@google.com
-amitmahajan@google.com
-jackyu@google.com
-fionaxu@google.com
-# more to add
-
-# VTS team
-yuexima@google.com
-dshi@google.com
\ No newline at end of file
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.5/vts/functional/Android.bp b/radio/1.5/vts/functional/Android.bp
index cd54d27..4549d3c 100644
--- a/radio/1.5/vts/functional/Android.bp
+++ b/radio/1.5/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioV1_5TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -36,5 +45,8 @@
         "android.hardware.radio.config@1.1",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"]
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/1.5/vts/functional/VtsHalRadioV1_5TargetTest.cpp b/radio/1.5/vts/functional/VtsHalRadioV1_5TargetTest.cpp
index 31466c5..d1461e3 100644
--- a/radio/1.5/vts/functional/VtsHalRadioV1_5TargetTest.cpp
+++ b/radio/1.5/vts/functional/VtsHalRadioV1_5TargetTest.cpp
@@ -16,6 +16,7 @@
 
 #include <radio_hidl_hal_utils_v1_5.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_5);
 INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_5,
                          testing::ValuesIn(android::hardware::getAllHalInstanceNames(
                                  android::hardware::radio::V1_5::IRadio::descriptor)),
diff --git a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
index 24b7fd5..0b49b36 100644
--- a/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
+++ b/radio/1.5/vts/functional/radio_hidl_hal_api.cpp
@@ -236,7 +236,12 @@
 
     ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSRSRP, rspInfo.error = %s\n",
           toString(radioRsp_v1_5->rspInfo.error).c_str());
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+    // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+    // setSignalStrengthReportingCriteria_1_5()
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+                             {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
 }
 
 /*
@@ -261,7 +266,12 @@
 
     ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSRSRQ, rspInfo.error = %s\n",
           toString(radioRsp_v1_5->rspInfo.error).c_str());
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+    // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+    // setSignalStrengthReportingCriteria_1_5()
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+                             {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
 }
 
 /*
@@ -307,7 +317,12 @@
 
     ALOGI("setSignalStrengthReportingCriteria_1_5_NGRAN_SSSINR, rspInfo.error = %s\n",
           toString(radioRsp_v1_5->rspInfo.error).c_str());
-    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error, {RadioError::NONE}));
+
+    // Allow REQUEST_NOT_SUPPORTED because some non-5G device may not support NGRAN for
+    // setSignalStrengthReportingCriteria_1_5()
+    ASSERT_TRUE(
+            CheckAnyOfErrors(radioRsp_v1_5->rspInfo.error,
+                             {RadioError::NONE, RadioError::REQUEST_NOT_SUPPORTED}));
 }
 
 /*
@@ -437,6 +452,7 @@
 TEST_P(RadioHidlTest_v1_5, togglingUiccApplicationsSimPresent) {
     // This test case only test SIM ABSENT case.
     if (cardStatus.base.base.base.cardState != CardState::PRESENT) return;
+    if (cardStatus.applications.size() == 0) return;
 
     // Disable Uicc applications.
     serial = GetRandomSerialNumber();
@@ -1265,7 +1281,7 @@
                      info.serviceType <= BarringInfo::ServiceType::OPERATOR_32));
         reportedServices.insert(info.serviceType);
 
-        // Any type that is "conditional" must have sane values for conditional barring
+        // Any type that is "conditional" must have valid values for conditional barring
         // factor and time.
         switch (info.barringType) {
             case BarringInfo::BarringType::NONE:  // fall through
@@ -1284,7 +1300,7 @@
 
     // Certain types of barring are relevant for certain RANs. Ensure that only the right
     // types are reported. Note that no types are required, simply that for a given technology
-    // only certain types are valid. This is one way to sanity check that implementations are
+    // only certain types are valid. This is one way to check that implementations are
     // not providing information that they don't have.
     static const std::set<BarringInfo::ServiceType> UTRA_SERVICES{
             BarringInfo::ServiceType::CS_SERVICE, BarringInfo::ServiceType::PS_SERVICE,
diff --git a/radio/1.6/Android.bp b/radio/1.6/Android.bp
new file mode 100644
index 0000000..4b79c0a
--- /dev/null
+++ b/radio/1.6/Android.bp
@@ -0,0 +1,32 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.radio@1.6",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IRadio.hal",
+        "IRadioIndication.hal",
+        "IRadioResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio@1.0",
+        "android.hardware.radio@1.1",
+        "android.hardware.radio@1.2",
+        "android.hardware.radio@1.3",
+        "android.hardware.radio@1.4",
+        "android.hardware.radio@1.5",
+        "android.hidl.base@1.0",
+        "android.hidl.safe_union@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/radio/1.6/IRadio.hal b/radio/1.6/IRadio.hal
new file mode 100644
index 0000000..e2d35d0
--- /dev/null
+++ b/radio/1.6/IRadio.hal
@@ -0,0 +1,585 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio@1.6;
+
+
+import @1.0::CdmaSmsMessage;
+import @1.0::Dial;
+import @1.0::GsmSmsMessage;
+import @1.1::CardPowerState;
+import @1.2::DataRequestReason;
+import @1.4::EmergencyCallRouting;
+import @1.4::EmergencyServiceCategory;
+import @1.4::RadioAccessFamily;
+import @1.5::IRadio;
+import @1.5::AccessNetwork;
+import @1.5::DataProfileInfo;
+import @1.5::LinkAddress;
+
+/**
+ * This interface is used by telephony and telecom to talk to cellular radio.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ * setResponseFunctions must work with @1.6::IRadioResponse and @1.6::IRadioIndication.
+ */
+interface IRadio extends @1.5::IRadio {
+    /**
+     * Toggle radio on and off (for "airplane" mode)
+     * If the radio is turned off/on the radio modem subsystem
+     * is expected return to an initialized state. For instance,
+     * any voice and data calls must be terminated and all associated
+     * lists emptied.
+     *
+     * When setting radio power on to exit from airplane mode to place an emergency call on this
+     * logical modem, powerOn, forEmergencyCall and preferredForEmergencyCall must be true. In
+     * this case, this modem is optimized to scan only emergency call bands, until:
+     * 1) Emergency call is completed; or
+     * 2) Another setRadioPower_1_5 is issued with forEmergencyCall being false or
+     * preferredForEmergencyCall being false; or
+     * 3) Timeout after 30 seconds if dial or emergencyDial is not called.
+     * Once one of these conditions is reached, the modem should move into normal operation.
+     *
+     * @param serial Serial number of request.
+     * @param powerOn To turn on radio -> on = true, to turn off radio -> on = false.
+     * @param forEmergencyCall To indication to radio if this request is due to emergency call.
+     *      No effect if powerOn is false.
+     * @param preferredForEmergencyCall indicate whether the following emergency call will be sent
+     *      on this modem or not. No effect if forEmergencyCall is false, or powerOn is false.
+     *
+     * Response callback is IRadioConfigResponse. setRadioPowerResponse_1_6.
+
+     * Note this API is the same as the 1.5
+     */
+    oneway setRadioPower_1_6(int32_t serial, bool powerOn, bool forEmergencyCall,
+            bool preferredForEmergencyCall);
+
+    /**
+     * Returns the data call list. An entry is added when a setupDataCall() is issued and removed
+     * on a deactivateDataCall(). The list is emptied when setRadioPower()  off/on issued or when
+     * the vendor HAL or modem crashes.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getDataCallListResponse_1_6()
+     */
+    oneway getDataCallList_1_6(int32_t serial);
+
+    /**
+     * Setup a packet data connection. If DataCallResponse.status returns DataCallFailCause:NONE,
+     * the data connection must be added to data calls and a unsolDataCallListChanged() must be
+     * sent. The call remains until removed by subsequent unsolDataCallIstChanged(). It may be
+     * lost due to many factors, including deactivateDataCall() being issued, the radio powered
+     * off, reception lost or even transient factors like congestion. This data call list is
+     * returned by getDataCallList() and dataCallListChanged().
+     *
+     * The Radio is expected to:
+     *   - Create one data call context.
+     *   - Create and configure a dedicated interface for the context.
+     *   - The interface must be point to point.
+     *   - The interface is configured with one or more addresses and is capable of sending and
+     *     receiving packets. The format is IP address with optional "/" prefix length
+     *     (The format is defined in RFC-4291 section 2.3). For example, "192.0.1.3",
+     *     "192.0.1.11/16", or "2001:db8::1/64". Typically one IPv4 or one IPv6 or one of each. If
+     *     the prefix length is absent, then the addresses are assumed to be point to point with
+     *     IPv4 with prefix length 32 or IPv6 with prefix length 128.
+     *   - Must not modify routing configuration related to this interface; routing management is
+     *     exclusively within the purview of the Android OS.
+     *   - Support simultaneous data call contexts up to DataRegStateResult.maxDataCalls specified
+     *     in the response of getDataRegistrationState.
+     *
+     * The differences relative to the 1.5 version of the API are:
+     *   - The addition of new parameters pduSessionId, sliceInfo, trafficDescriptor, and
+     *     matchAllRuleAllowed.
+     *   - If an existing data call should be used for the request, that must be indicated in the
+     *     response by setting SetupDataCallResult::cid to the context id of that call.
+     *
+     * @param serial Serial number of request.
+     * @param accessNetwork The access network to setup the data call. If the data connection cannot
+     *     be established on the specified access network then it should be responded with an error.
+     * @param dataProfileInfo Data profile info.
+     * @param roamingAllowed Indicates whether or not data roaming is allowed by the user.
+     * @param reason The request reason. Must be DataRequestReason:NORMAL or
+     *     DataRequestReason:HANDOVER.
+     * @param addresses If the reason is DataRequestReason:HANDOVER, this indicates the list of link
+     *     addresses of the existing data connection. This parameter must be ignored unless reason
+     *     is DataRequestReason:HANDOVER.
+     * @param dnses If the reason is DataRequestReason:HANDOVER, this indicates the list of DNS
+     *     addresses of the existing data connection. The format is defined in RFC-4291 section 2.2.
+     *     For example, "192.0.1.3" or "2001:db8::1". This parameter must be ignored unless reason
+     *     is DataRequestReason:HANDOVER.
+     * @param pduSessionId The pdu session id to be used for this data call.  A value of 0 means
+     *     no pdu session id was attached to this call.
+     *     Reference: 3GPP TS 24.007 section 11.2.3.1b
+     * @param sliceInfo SliceInfo to be used for the data connection when a handover occurs from
+     *     EPDG to 5G.  It is valid only when accessNetwork is AccessNetwork:NGRAN.  If the slice
+     *     passed from EPDG is rejected, then the data failure cause must be
+     *     DataCallFailCause:SLICE_REJECTED.
+     * @param trafficDescriptor TrafficDescriptor for which data connection needs to be
+     *     established. It is used for URSP traffic matching as described in TS 24.526
+     *     Section 4.2.2. It includes an optional DNN which, if present, must be used for traffic
+     *     matching -- it does not specify the end point to be used for the data call. The end
+     *     point is specified by DataProfileInfo.apn; DataProfileInfo.apn must be used as the end
+     *     point if one is not specified through URSP rules.
+     * @param matchAllRuleAllowed bool to indicate if using default match-all URSP rule for this
+     *     request is allowed. If false, this request must not use the match-all URSP rule and if
+     *     a non-match-all rule is not found (or if URSP rules are not available) it should return
+     *     failure with cause DataCallFailCause:MATCH_ALL_RULE_NOT_ALLOWED. This is needed as some
+     *     requests need to have a hard failure if the intention cannot be met, for example, a
+     *     zero-rating slice.
+     *
+     * Response function is IRadioResponse.setupDataCallResponse_1_6()
+     *
+     */
+    oneway setupDataCall_1_6(int32_t serial, AccessNetwork accessNetwork,
+            DataProfileInfo dataProfileInfo, bool roamingAllowed,
+            DataRequestReason reason, vec<LinkAddress> addresses, vec<string> dnses,
+            int32_t pduSessionId, OptionalSliceInfo sliceInfo,
+            OptionalTrafficDescriptor trafficDescriptor, bool matchAllRuleAllowed);
+
+    /**
+     * Send an SMS message
+     *
+     * @param serial Serial number of request.
+     * @param message GsmSmsMessage as defined in types.hal
+     *
+     * Response function is IRadioResponse.sendSmsResponse_1_6()
+     *
+     * Note this API is the same as the 1.0
+     *
+     * Based on the return error, caller decides to resend if sending sms
+     * fails. RadioError:SMS_SEND_FAIL_RETRY means retry (i.e. error cause is 332)
+     * and RadioError:GENERIC_FAILURE means no retry (i.e. error cause is 500)
+     */
+    oneway sendSms_1_6(int32_t serial, GsmSmsMessage message);
+
+    /**
+     * Send an SMS message. Identical to sendSms_1_6,
+     * except that more messages are expected to be sent soon. If possible,
+     * keep SMS relay protocol link open (eg TS 27.005 AT+CMMS command)
+     *
+     * @param serial Serial number of request.
+     * @param message GsmSmsMessage as defined in types.hal
+     *
+     * Response function is IRadioResponse.sendSmsExpectMoreResponse_1_6()
+     *
+     * Note this API is the same as the 1.0
+     *
+     * Based on the return error, caller decides to resend if sending sms
+     * fails. RadioError:SMS_SEND_FAIL_RETRY means retry (i.e. error cause is 332)
+     * and RadioError:GENERIC_FAILURE means no retry (i.e. error cause is 500)
+     */
+    oneway sendSmsExpectMore_1_6(int32_t serial, GsmSmsMessage message);
+
+    /**
+     * Send a CDMA SMS message
+     *
+     * @param serial Serial number of request.
+     * @param sms Cdma Sms to be sent described by CdmaSmsMessage in types.hal
+     *
+     * Response callback is IRadioResponse.sendCdmaSmsResponse_1_6()
+     *
+     * Note this API is the same as the 1.0
+     *
+     */
+    oneway sendCdmaSms_1_6(int32_t serial, CdmaSmsMessage sms);
+
+    /**
+     * Send an SMS message. Identical to sendCdmaSms_1_6,
+     * except that more messages are expected to be sent soon.
+     *
+     * @param serial Serial number of request.
+     * @param sms Cdma Sms to be sent described by CdmaSmsMessage in types.hal
+     *
+     * Response callback is IRadioResponse.sendCdmaSMSExpectMoreResponse_1_6()
+     *
+     * Note this API is the same as the 1.5
+     *
+     */
+    oneway sendCdmaSmsExpectMore_1_6(int32_t serial, CdmaSmsMessage sms);
+
+    /**
+     * Set SIM card power state.
+     * Request is used to power off or power on the card. It should not generate
+     * a CardState.CARDSTATE_ABSENT indication, since the SIM is still physically
+     * inserted.
+     *
+     * @param serial Serial number of request
+     * @param powerUp POWER_DOWN if powering down the SIM card,
+     *                POWER_UP if powering up the SIM card,
+     *                POWER_UP_PASS_THROUGH if powering up the SIM card in
+     *                                      pass through mode.
+     *
+     * When SIM card is in POWER_UP_PASS_THROUGH, the modem does not send
+     * any command to it (for example SELECT of MF, or TERMINAL
+     * CAPABILITY), and the SIM card is controlled completely by Telephony
+     * sending APDUs directly. The SIM card state must be
+     * RIL_CARDSTATE_PRESENT and the number of card apps will be 0.
+     * No new error code is generated. Emergency calls are supported in
+     * the same way as if the SIM card is absent.
+     * Pass-through mode is valid only for the specific card session where
+     * it is activated, and normal behavior occurs at the next SIM
+     * initialization, unless POWER_UP_PASS_THROUGH is requested again.
+     *
+     * The device is required to power down the SIM card before it can
+     * switch the mode between POWER_UP and POWER_UP_PASS_THROUGH.
+     * At device power up, the SIM interface is powered up automatically.
+     * Each subsequent request to this method is processed only after the
+     * completion of the previous one.
+     *
+     * When the SIM is in POWER_DOWN, the modem should send an empty vector of
+     * AppStatus in CardStatus.applications. If a SIM in the POWER_DOWN state
+     * is removed and a new SIM is inserted, the new SIM should be in POWER_UP
+     * mode by default. If the device is turned off or restarted while the SIM
+     * is in POWER_DOWN, then the SIM should turn on normally in POWER_UP mode
+     * when the device turns back on.
+     *
+     * Response callback is IRadioResponse.setSimCardPowerResponse_1_6().
+     * Note that this differs from setSimCardPower_1_1 in that the response
+     * callback should only be sent once the device has finished executing
+     * the request (the SIM has finished powering on or off).
+     */
+    oneway setSimCardPower_1_6(int32_t serial, CardPowerState powerUp);
+
+    /**
+     * Enable or disable E-UTRA-NR dual connectivity. If disabled then UE will not connect
+     * to secondary carrier.
+     *
+     * @param serial Serial number of request.
+     * @param nrDualConnectivityState expected NR dual connectivity state.
+     * 1. Enable NR dual connectivity {NrDualConnectivityState:ENABLE}
+     * 2. Disable NR dual connectivity {NrDualConnectivityState:DISABLE}
+     * 3. Disable NR dual connectivity and force secondary cell to be released
+     * {NrDualConnectivityState:DISABLE_IMMEDIATE}
+     *
+     * Response callback is IRadioResponse.setNRDualConnectivityStateResponse()
+     */
+    oneway setNrDualConnectivityState(int32_t serial,
+            NrDualConnectivityState nrDualConnectivityState);
+
+    /**
+     * Is E-UTRA-NR Dual Connectivity enabled
+     *
+     * @param serial Serial number of request.
+     * Response callback is IRadioResponse.isNRDualConnectivityEnabledResponse()
+     */
+    oneway isNrDualConnectivityEnabled(int32_t serial);
+
+    /**
+     * Reserves an unallocated pdu session id from the pool of ids.
+     *
+     * The allocated id is returned in the response.
+     *
+     * When the id is no longer needed, call releasePduSessionId to
+     * return it to the pool.
+     *
+     * Reference: 3GPP TS 24.007 section 11.2.3.1b
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.allocatePduSessionIdResponse()
+     */
+    oneway allocatePduSessionId(int32_t serial);
+
+    /**
+     * Releases a pdu session id that was previously allocated using
+     * allocatePduSessionId.
+     *
+     * Reference: 3GPP TS 24.007 section 11.2.3.1b
+     * @param serial Serial number of request.
+     * @param id Pdu session id to release.
+     *
+     * Response function is IRadioResponse.releasePduSessionIdResponse()
+     */
+    oneway releasePduSessionId(int32_t serial, int32_t id);
+
+    /**
+     * Indicates that a handover to the IWLAN transport has begun.
+     *
+     * Any resources being transferred to the IWlan transport cannot be released while a
+     * handover is underway. For example, if a pdu session id needs to be
+     * transferred to IWlan, then, the modem should not release the id while
+     * the handover is in progress.
+     *
+     * If a handover was unsuccessful, then the framework calls IRadio::cancelHandover.
+     * The modem retains ownership over any of the resources being transferred to IWlan.
+     *
+     * If a handover was successful, the framework calls IRadio::deactivateDataCall with reason
+     * HANDOVER. The IWlan transport now owns the transferred resources and is responsible for
+     * releasing them.
+     *
+     * @param serial Serial number of request.
+     * @param id callId The identifier of the data call which is provided in SetupDataCallResult
+     *
+     * Response function is IRadioResponse.startHandoverResponse()
+     */
+    oneway startHandover(int32_t serial, int32_t callId);
+
+    /**
+     * Indicates that a handover was cancelled after a call to IRadio::startHandover.
+     *
+     * Since the handover was unsuccessful, the modem retains ownership over any of the resources
+     * being transferred and is still responsible for releasing them.
+     *
+     * @param serial Serial number of request.
+     * @param id callId The identifier of the data call which is provided in SetupDataCallResult
+     *
+     * Response function is IRadioResponse.cancelHandoverResponse()
+     */
+    oneway cancelHandover(int32_t serial, int32_t callId);
+
+    /**
+     * Requests to set the network type for searching and registering.
+     *
+     * Instruct the radio to *only* accept the types of network provided.
+     * setPreferredNetworkType, setPreferredNetworkTypesBitmap will not be called anymore
+     * except for IRadio v1.5 or older devices.
+     *
+     * In case of an emergency call, the modem is authorized to bypass this
+     * restriction.
+     *
+     * @param serial Serial number of request.
+     * @param networkTypeBitmap a 32-bit bearer bitmap of RadioAccessFamily
+     *
+     * Response callback is IRadioResponse.setAllowedNetworkTypesBitmapResponse()
+     */
+    oneway setAllowedNetworkTypesBitmap(
+            uint32_t serial, bitfield<RadioAccessFamily> networkTypeBitmap);
+
+    /**
+     * Requests bitmap representing the currently allowed network types.
+     *
+     * getPreferredNetworkType, getPreferredNetworkTypesBitmap will not be called anymore
+     * except for IRadio v1.5 or older devices.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getAllowedNetworkTypesBitmapResponse()
+     */
+    oneway getAllowedNetworkTypesBitmap(int32_t serial);
+
+    /**
+     * Control data throttling at modem.
+     *   - DataThrottlingAction:NO_DATA_THROTTLING should clear any existing
+     *     data throttling within the requested completion window.
+     *   - DataThrottlingAction:THROTTLE_SECONDARY_CARRIER: Remove any existing
+     *     throttling on anchor carrier and achieve maximum data throttling on
+     *     secondary carrier within the requested completion window.
+     *   - DataThrottlingAction:THROTTLE_ANCHOR_CARRIER: disable secondary
+     *     carrier and achieve maximum data throttling on anchor carrier by
+     *     requested completion window.
+     *   - DataThrottlingAction:HOLD: Immediately hold on to current level of
+     *     throttling.
+     *
+     * @param serial Serial number of request.
+     * @param dataThrottlingAction DataThrottlingAction as defined in types.hal
+     * @param completionDurationMillis window, in milliseconds, in which the
+     *     requested throttling action has to be achieved. This must be 0 when
+     *     dataThrottlingAction is DataThrottlingAction:HOLD.
+     *
+     * Response function is IRadioResponse.setDataThrottlingResponse()
+     */
+    oneway setDataThrottling(int32_t serial,
+            DataThrottlingAction dataThrottlingAction,
+            int64_t completionDurationMillis);
+
+    /**
+     * Initiate emergency voice call, with zero or more emergency service category(s), zero or
+     * more emergency Uniform Resource Names (URN), and routing information for handling the call.
+     * Android uses this request to make its emergency call instead of using @1.0::IRadio.dial
+     * if the 'address' in the 'dialInfo' field is identified as an emergency number by Android.
+     *
+     * In multi-sim scenario, if the emergency number is from a specific subscription, this radio
+     * request can still be sent out on the other subscription as long as routing is set to
+     * @1.4::EmergencyNumberRouting#EMERGENCY. This radio request will not be sent on an inactive
+     * (PIN/PUK locked) subscription unless both subscriptions are PIN/PUK locked. In this case,
+     * the request will be sent on the primary subscription.
+     *
+     * Some countries or carriers require some emergency numbers that must be handled with normal
+     * call routing if possible or emergency routing. 1) if the 'routing' field is specified as
+     * @1.4::EmergencyNumberRouting#NORMAL, the implementation must try the full radio service to
+     * use normal call routing to handle the call; if service cannot support normal routing, the
+     * implementation must use emergency routing to handle the call. 2) if 'routing' is specified
+     * as @1.4::EmergencyNumberRouting#EMERGENCY, the implementation must use emergency routing to
+     * handle the call. 3) if 'routing' is specified as @1.4::EmergencyNumberRouting#UNKNOWN,
+     * Android does not know how to handle the call.
+     *
+     * If the dialed emergency number does not have a specified emergency service category, the
+     * 'categories' field is set to @1.4::EmergencyServiceCategory#UNSPECIFIED; if the dialed
+     * emergency number does not have specified emergency Uniform Resource Names, the 'urns' field
+     * is set to an empty list. If the underlying technology used to request emergency services
+     * does not support the emergency service category or emergency uniform resource names, the
+     * field 'categories' or 'urns' may be ignored.
+     *
+     * In the scenarios that the 'address' in the 'dialInfo' field has other functions besides the
+     * emergency number function, if the 'hasKnownUserIntentEmergency' field is true, the user's
+     * intent for this dial request is emergency call, and the modem must treat this as an actual
+     * emergency dial; if the 'hasKnownUserIntentEmergency' field is false, Android does not know
+     * user's intent for this call.
+     *
+     * If 'isTesting' is true, this request is for testing purpose, and must not be sent to a real
+     * emergency service; otherwise it's for a real emergency call request.
+     *
+     * Reference: 3gpp 22.101, Section 10 - Emergency Calls;
+     *            3gpp 23.167, Section 6 - Functional description;
+     *            3gpp 24.503, Section 5.1.6.8.1 - General;
+     *            RFC 5031
+     *
+     * @param serial Serial number of request.
+     * @param dialInfo the same @1.0::Dial information used by @1.0::IRadio.dial.
+     * @param categories bitfield<@1.4::EmergencyServiceCategory> the Emergency Service Category(s)
+     *     of the call.
+     * @param urns the emergency Uniform Resource Names (URN)
+     * @param routing @1.4::EmergencyCallRouting the emergency call routing information.
+     * @param hasKnownUserIntentEmergency Flag indicating if user's intent for the emergency call
+     *     is known.
+     * @param isTesting Flag indicating if this request is for testing purpose.
+     *
+     * Response function is IRadioResponse.emergencyDialResponse()
+     */
+    oneway emergencyDial_1_6(int32_t serial, Dial dialInfo,
+            bitfield<EmergencyServiceCategory> categories, vec<string> urns,
+            EmergencyCallRouting routing, bool hasKnownUserIntentEmergency, bool isTesting);
+
+    /**
+     * Get which bands the modem's background scan is acting on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getSystemSelectionChannelsResponse()
+     */
+    oneway getSystemSelectionChannels(int32_t serial);
+
+   /**
+     * Request all of the current cell information known to the radio. The radio
+     * must return list of all current cells, including the neighboring cells. If for a particular
+     * cell information isn't known then the appropriate unknown value will be returned.
+     * This does not cause or change the rate of unsolicited cellInfoList().
+     *
+     * This is identical to getCellInfoList in V1.0, but it requests updated version of CellInfo.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getCellInfoListResponse()
+     */
+    oneway getCellInfoList_1_6(int32_t serial);
+
+    /**
+     * Request current voice registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getVoiceRegistrationStateResponse_1_6()
+     */
+    oneway getVoiceRegistrationState_1_6(int32_t serial);
+
+    /**
+     * Requests current signal strength and associated information.  Must succeed if radio is on.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getSignalStrengthResponse_1_6()
+     */
+    oneway getSignalStrength_1_6(int32_t serial);
+
+    /**
+     * Request current data registration state.
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getDataRegistrationStateResponse_1_6()
+     */
+    oneway getDataRegistrationState_1_6(int32_t serial);
+
+    /**
+     * Requests current call list
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is IRadioResponse.getCurrentCallsResponse_1_6()
+     */
+    oneway getCurrentCalls_1_6(int32_t serial);
+
+    /**
+     * Request to get the current slicing configuration including URSP rules and
+     * NSSAIs (configured, allowed and rejected).
+     * URSP stands for UE route selection policy and is defined in 3GPP TS 24.526
+     * Section 4.2.
+     * An NSSAI is a collection of network slices. Each network slice is identified by
+     * an S-NSSAI and is represented by the struct SliceInfo. NSSAI and S-NSSAI
+     * are defined in 3GPP TS 24.501.
+     *
+     * Response function is IRadioResponse.getSlicingConfigResponse()
+     */
+    oneway getSlicingConfig(int32_t serial);
+
+    /**
+     * Provide Carrier specific information to the modem that must be used to
+     * encrypt the IMSI and IMPI. Sent by the framework during boot, carrier
+     * switch and everytime the framework receives a new certificate.
+     *
+     * @param serial Serial number of request.
+     * @param imsiEncryptionInfo ImsiEncryptionInfo as defined in types.hal.
+     *
+     * Response callback is
+     * IRadioResponse.setCarrierInfoForImsiEncryptionResponse()
+     *
+     * Note this API is the same as the 1.1 version except using the 1.6 ImsiEncryptionInfo
+     * as the input param.
+     */
+    oneway setCarrierInfoForImsiEncryption_1_6(int32_t serial, @1.6::ImsiEncryptionInfo imsiEncryptionInfo);
+
+    /**
+     * Get the local and global phonebook records from the SIM card.
+     * This should be called again after a simPhonebookChanged notification is received.
+     *
+     * The phonebook records are received via IRadioIndication.simPhonebookRecordsReceived()
+     *
+     * @param serial Serial number of request.
+     *
+     * Response callback is IRadioResponse.getSimPhonebookRecordsResponse()
+     */
+    oneway getSimPhonebookRecords(int32_t serial);
+
+    /**
+     * Get the phone book capacity
+     *
+     * @param serial Serial number of request.
+     *
+     * Response function is defined from IRadioResponse.getSimPhonebookCapacityResponse()
+     */
+    oneway getSimPhonebookCapacity(int32_t serial);
+
+    /**
+     * Insert, delete or update a phonebook record on the SIM card.
+     * If the index of recordInfo is 0, the phonebook record will be added to global or
+     * local phonebook, and global phonebook has higher priority than local phonebook.
+     *
+     * If the fields in the recordInfo are all empty except for the index, the phonebook
+     * record specified by the index will be deleted.
+     *
+     * The indication simPhonebookChanged will be called after every successful call of
+     * updateSimPhonebookRecords.
+     *
+     * @param serial Serial number of request.
+     * @param recordInfo Details of the record to insert, delete or update.
+     *
+     * Response callback is IRadioResponse.updateSimPhonebookRecordsResponse()
+     */
+    oneway updateSimPhonebookRecords(int32_t serial, PhonebookRecordInfo recordInfo);
+};
diff --git a/radio/1.6/IRadioIndication.hal b/radio/1.6/IRadioIndication.hal
new file mode 100644
index 0000000..05a7585
--- /dev/null
+++ b/radio/1.6/IRadioIndication.hal
@@ -0,0 +1,140 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio@1.6;
+
+import @1.0::RadioIndicationType;
+import @1.5::IRadioIndication;
+import @1.6::CellInfo;
+import @1.6::LinkCapacityEstimate;
+import @1.6::NetworkScanResult;
+import @1.6::SignalStrength;
+import @1.6::SetupDataCallResult;
+import @1.6::PbReceivedStatus;
+import @1.6::PhysicalChannelConfig;
+import @1.6::PhonebookRecordInfo;
+
+/**
+ * Interface declaring unsolicited radio indications.
+ */
+interface IRadioIndication extends @1.5::IRadioIndication {
+
+    /**
+     * Indicates data call contexts have changed.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * SetupDataCallResult.
+     *
+     * @param type Type of radio indication
+     * @param dcList Array of SetupDataCallResult identical to that returned by
+     *        IRadio.getDataCallList(). It is the complete list of current data contexts including
+     *        new contexts that have been activated. A data call is only removed from this list
+     *        when any of the below conditions is matched.
+     *        1. The framework sends a IRadio.deactivateDataCall().
+     *        2. The radio is powered off/on.
+     *        3. Unsolicited disconnect from either modem or network side.
+     */
+    oneway dataCallListChanged_1_6(RadioIndicationType type, vec<SetupDataCallResult> dcList);
+
+    /**
+     * The modem can explicitly set SetupDataCallResult::suggestedRetryTime after a failure in
+     * IRadio@1.6::SetupDataCall. During that time, no new calls are allowed to
+     * IRadio@1.6::SetupDataCall that use the same APN.
+     *
+     * When IRadioIndication@1.6::unthrottleApn is sent, AOSP will no longer throttle calls
+     * to IRadio@1.6::SetupDataCall for the given APN.
+     *
+     * @param type Type of radio indication
+     * @param apn Apn to unthrottle
+     */
+    oneway unthrottleApn(RadioIndicationType type, string apn);
+
+    /**
+     * Indicates current link capacity estimate.
+     * This replaces @1.2::IRadioIndication.currentLinkCapacityEstimate().
+     * This indication is sent whenever the reporting criteria, as set by
+     * @1.2::IRadio.setLinkCapacityReportingCriteria, are met and the indication is not
+     * suppressed by @1.2::IRadio.setIndicationFilter_1_2().
+     *
+     * @param type Type of radio indication
+     * @param lce LinkCapacityEstimate
+     */
+    oneway currentLinkCapacityEstimate_1_6(RadioIndicationType type, LinkCapacityEstimate lce);
+
+    /**
+     * Indicates current signal strength of the radio.
+     *
+     * This is identical to currentSignalStrength_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param type Type of radio indication
+     * @param signalStrength SignalStrength information
+     */
+    oneway currentSignalStrength_1_6(RadioIndicationType type, SignalStrength signalStrength);
+
+    /**
+     * Report all of the current cell information known to the radio.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     *
+     * @param type Type of radio indication
+     * @param records Current cell information
+     */
+    oneway cellInfoList_1_6(RadioIndicationType type, vec<CellInfo> records);
+
+    /**
+     * Incremental network scan results.
+     *
+     * This indication is updated from IRadioIndication@1.5 to report the @1.6 version of
+     * CellInfo.
+     */
+    oneway networkScanResult_1_6(RadioIndicationType type, NetworkScanResult result);
+
+    /**
+     * Indicates physical channel configurations.
+     *
+     * An empty configs list shall be returned when the radio is in idle mode (i.e. RRC idle).
+     *
+     * @param type Type of radio indication
+     * @param configs Vector of PhysicalChannelConfigs
+     */
+    oneway currentPhysicalChannelConfigs_1_6(RadioIndicationType type,
+            vec<PhysicalChannelConfig> configs);
+
+    /**
+     * Indicates whether SIM phonebook is changed.
+     *
+     * This indication is sent whenever the SIM phonebook is changed, including SIM is
+     * inserted or removed and updated by IRadio.updateSimPhonebookRecords.
+     *
+     * @param type Type of radio indication
+     */
+    oneway simPhonebookChanged(RadioIndicationType type);
+
+    /**
+     * Indicates the content of all the used records in the SIM phonebook.
+     *
+     * This indication is associated with the API getSimPhonebookRecords and
+     * might be received more than once that is replying on the record count.
+     *
+     * @param type Type of radio indication
+     * @param status Status of PbReceivedStatus
+     * @param records Vector of PhonebookRecordInfo
+     */
+    oneway simPhonebookRecordsReceived(RadioIndicationType type,
+            PbReceivedStatus status, vec<PhonebookRecordInfo> records);
+};
diff --git a/radio/1.6/IRadioResponse.hal b/radio/1.6/IRadioResponse.hal
new file mode 100644
index 0000000..a1286a5
--- /dev/null
+++ b/radio/1.6/IRadioResponse.hal
@@ -0,0 +1,494 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio@1.6;
+
+import @1.0::SendSmsResult;
+import @1.4::RadioAccessFamily;
+import @1.5::IRadioResponse;
+import @1.5::RadioAccessSpecifier;
+import @1.6::Call;
+import @1.6::CellInfo;
+import @1.6::RegStateResult;
+import @1.6::RadioResponseInfo;
+import @1.6::SetupDataCallResult;
+import @1.6::SignalStrength;
+import @1.6::SlicingConfig;
+import @1.6::PhonebookCapacity;
+
+/**
+ * Interface declaring response functions to solicited radio requests.
+ */
+interface IRadioResponse extends @1.5::IRadioResponse {
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:RF_HARDWARE_ISSUE
+     *   RadioError:NO_RF_CALIBRATION_INFO
+     */
+    oneway setRadioPowerResponse_1_6(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dcResponse SetupDataCallResult defined in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE must be returned on both success and failure of setup with the
+     *              DataCallResponse.status containing the actual status
+     *              For all other errors the DataCallResponse is ignored.
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:OP_NOT_ALLOWED_BEFORE_REG_TO_NW
+     *   RadioError:OP_NOT_ALLOWED_DURING_VOICE_CALL
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_RESOURCES if the vendor is unable handle due to resources
+     *              are full.
+     *   RadioError:SIM_ABSENT
+     */
+    oneway setupDataCallResponse_1_6(RadioResponseInfo info, SetupDataCallResult dcResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dcResponse List of SetupDataCallResult as defined in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:SIM_ABSENT
+     */
+    oneway getDataCallListResponse_1_6(RadioResponseInfo info, vec<SetupDataCallResult> dcResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:SMS_SEND_FAIL_RETRY
+     *   RadioError:NETWORK_REJECT
+     *   RadioError:INVALID_STATE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_RATE_LIMITED
+     *   RadioError:INVALID_SMS_FORMAT
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:ENCODING_ERR
+     *   RadioError:INVALID_SMSC_ADDRESS
+     *   RadioError:MODEM_ERR
+     *   RadioError:NETWORK_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_MODEM_STATE
+     *   RadioError:NETWORK_NOT_READY
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *   RadioError:SIM_ABSENT
+     *   RadioError:ACCESS_BARRED
+     *   RadioError:BLOCKED_DUE_TO_CALL
+     */
+    oneway sendSmsResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:SMS_SEND_FAIL_RETRY
+     *   RadioError:NETWORK_REJECT
+     *   RadioError:INVALID_STATE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_RATE_LIMITED
+     *   RadioError:INVALID_SMS_FORMAT
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:FDN_CHECK_FAILURE
+     *   RadioError:ENCODING_ERR
+     *   RadioError:INVALID_SMSC_ADDRESS
+     *   RadioError:MODEM_ERR
+     *   RadioError:NETWORK_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_MODEM_STATE
+     *   RadioError:NETWORK_NOT_READY
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *   RadioError:SIM_ABSENT
+     *   RadioError:ACCESS_BARRED
+     *   RadioError:BLOCKED_DUE_TO_CALL
+     */
+    oneway sendSmsExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param sms Sms result struct as defined by SendSmsResult in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:SMS_SEND_FAIL_RETRY
+     *   RadioError:NETWORK_REJECT
+     *   RadioError:INVALID_STATE
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_RATE_LIMITED
+     *   RadioError:INVALID_SMS_FORMAT
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:FDN_CHECK_FAILURE
+     *   RadioError:MODEM_ERR
+     *   RadioError:NETWORK_ERR
+     *   RadioError:ENCODING_ERR
+     *   RadioError:INVALID_SMSC_ADDRESS
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:ENCODING_ERR
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *   RadioError:SIM_ABSENT
+     *   RadioError:SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED
+     *   RadioError:ACCESS_BARRED
+     *   RadioError:BLOCKED_DUE_TO_CALL
+     */
+    oneway sendCdmaSmsResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param sms Response to sms sent as defined by SendSmsResult in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:SMS_SEND_FAIL_RETRY
+     *   RadioError:NETWORK_REJECT
+     *   RadioError:INVALID_STATE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:NO_MEMORY
+     *   RadioError:REQUEST_RATE_LIMITED
+     *   RadioError:INVALID_SMS_FORMAT
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:FDN_CHECK_FAILURE
+     *   RadioError:ENCODING_ERR
+     *   RadioError:INVALID_SMSC_ADDRESS
+     *   RadioError:MODEM_ERR
+     *   RadioError:NETWORK_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_MODEM_STATE
+     *   RadioError:NETWORK_NOT_READY
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     *   RadioError:SIM_ABSENT
+     *   RadioError:SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED
+     *   RadioError:ACCESS_BARRED
+     *   RadioError:BLOCKED_DUE_TO_CALL
+     */
+    oneway sendCdmaSmsExpectMoreResponse_1_6(RadioResponseInfo info, SendSmsResult sms);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:SIM_ERR (indicates a timeout or other issue making the SIM unresponsive)
+     *
+     * Note that this differs from setSimCardPowerResponse_1_1 in that the response
+     * should only be sent once the request from setSimCardPower_1_6 is complete
+     * (the SIM has finished powering on or off).
+     */
+    oneway setSimCardPowerResponse_1_6(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_STATE
+     */
+    oneway setNrDualConnectivityStateResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * @param isEnabled Indicates whether NR dual connectivity is enabled or not, True if enabled
+     *        else false.
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    oneway isNrDualConnectivityEnabledResponse(RadioResponseInfo info, bool isEnabled);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param id The allocated id. On an error, this is set to 0.
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_RESOURCES- Indicates that no pdu session ids are available
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    oneway allocatePduSessionIdResponse(RadioResponseInfo info, int32_t id);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_RESOURCES
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     */
+    oneway releasePduSessionIdResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_RESOURCES
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_CALL_ID
+     */
+    oneway startHandoverResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dcResponse Attributes of data call
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NO_RESOURCES
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_CALL_ID
+     */
+    oneway cancelHandoverResponse(RadioResponseInfo info);
+
+    /**
+     * Callback of IRadio.setAllowedNetworkTypesBitmap(int, bitfield<RadioAccessFamily>)
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:MODE_NOT_SUPPORTED
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     */
+    oneway setAllowedNetworkTypesBitmapResponse(RadioResponseInfo info);
+
+    /**
+     * Callback of IRadio.getAllowedNetworkTypesBitmap(int, bitfield<RadioAccessFamily>)
+     * @param info Response info struct containing response type, serial no. and error
+     * @param networkTypeBitmap a 32-bit bitmap of RadioAccessFamily.
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:OPERATION_NOT_ALLOWED
+     *   RadioError:MODE_NOT_SUPPORTED
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:MODEM_ERR
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     */
+    oneway getAllowedNetworkTypesBitmapResponse(
+            RadioResponseInfo info, bitfield<RadioAccessFamily> networkTypeBitmap);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     *
+     *  Valid errors returned:
+     *  RadioError:NONE
+     *  RadioError:RADIO_NOT_AVAILABLE
+     *  RadioError:MODEM_ERR
+     *  RadioError:INVALID_ARGUMENTS
+     *  RadioError:REQUEST_NOT_SUPPORTED
+     */
+    oneway setDataThrottlingResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param specifiers List of RadioAccessSpecifiers that are scanned.
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     */
+    oneway getSystemSelectionChannelsResponse(
+            RadioResponseInfo info, vec<RadioAccessSpecifier> specifiers);
+
+    /**
+     * This is identical to getCellInfoListResponse_1_5 but uses an updated version of CellInfo.
+     *
+     * @param info Response info struct containing response type, serial no. and error
+     * @param cellInfo List of current cell information known to radio
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getCellInfoListResponse_1_6(RadioResponseInfo info, vec<CellInfo> cellInfo);
+
+    /**
+     * This is identical to getSignalStrengthResponse_1_4 but uses an updated version of
+     * SignalStrength.
+     *
+     * @param signalStrength Current signal strength
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getSignalStrengthResponse_1_6(RadioResponseInfo info, SignalStrength signalStrength);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param voiceRegResponse Current Voice registration response as defined by RegStateResult
+     *        in types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getVoiceRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult voiceRegResponse);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param dataRegResponse Current Data registration response as defined by RegStateResult in
+     *        types.hal
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:NOT_PROVISIONED
+     */
+    oneway getDataRegistrationStateResponse_1_6(RadioResponseInfo info,
+            RegStateResult dataRegResponse);
+
+    /**
+     * @param calls Current call list
+     *   RadioError:NO_MEMORY
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:SYSTEM_ERR
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:NO_RESOURCES
+     *   RadioError:CANCELLED
+     */
+    oneway getCurrentCallsResponse_1_6(RadioResponseInfo info, vec<Call> calls);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param slicingConfig Current slicing configuration
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:MODEM_ERR
+     */
+    oneway getSlicingConfigResponse(RadioResponseInfo info,
+            SlicingConfig slicingConfig);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:INVALID_SIM_STATE
+     *   RadioError:MODEM_ERR
+     *   RadioError:INTERNAL_ERR
+     * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+     * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+     */
+    oneway getSimPhonebookRecordsResponse(RadioResponseInfo info);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param capacity Response capacity enum indicating response processing status
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:INVALID_SIM_STATE
+     *   RadioError:MODEM_ERR
+     *   RadioError:INTERNAL_ERR
+     * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+     * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+     */
+    oneway getSimPhonebookCapacityResponse(RadioResponseInfo info, PhonebookCapacity capacity);
+
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param updatedRecordIndex The index of the updated or inserted record in the phonebook and
+     *                           the minimum value is 1
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:REQUEST_NOT_SUPPORTED
+     *   RadioError:INVALID_ARGUMENTS
+     *   RadioError:INVALID_SIM_STATE
+     *   RadioError:MODEM_ERR
+     *   RadioError:INTERNAL_ERR
+     *   RadioError:SIM_ERR
+     *   RadioError:NO_SUCH_ENTRY
+     *   RadioError:NO_RESOURCES
+     * REQUEST_NOT_SUPPORTED may only be returned on devices that don't support this API,
+     * indicated by the HAL capability CAPABILITY_SIM_PHONEBOOK_IN_MODEM.
+     */
+    oneway updateSimPhonebookRecordsResponse(RadioResponseInfo info, int32_t updatedRecordIndex);
+};
diff --git a/radio/1.6/types.hal b/radio/1.6/types.hal
new file mode 100644
index 0000000..4b75db5
--- /dev/null
+++ b/radio/1.6/types.hal
@@ -0,0 +1,1208 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio@1.6;
+
+import @1.0::CdmaSignalStrength;
+import @1.0::EvdoSignalStrength;
+import @1.0::GsmSignalStrength;
+import @1.0::LteSignalStrength;
+import @1.0::RadioError;
+import @1.0::RadioResponseType;
+import @1.0::RegState;
+import @1.1::EutranBands;
+import @1.1::GeranBands;
+import @1.1::ScanStatus;
+import @1.1::UtranBands;
+import @1.1::ImsiEncryptionInfo;
+import @1.2::Call;
+import @1.2::CellInfoCdma;
+import @1.2::CellConnectionStatus;
+import @1.2::TdscdmaSignalStrength;
+import @1.2::WcdmaSignalStrength;
+import @1.4::DataCallFailCause;
+import @1.4::DataConnActiveStatus;
+import @1.4::NrSignalStrength;
+import @1.4::PdpProtocolType;
+import @1.4::RadioTechnology;
+import @1.5::CellIdentity;
+import @1.5::CellIdentityLte;
+import @1.5::CellIdentityNr;
+import @1.5::CellInfoGsm;
+import @1.5::CellInfoWcdma;
+import @1.5::CellInfoTdscdma;
+import @1.5::LinkAddress;
+import @1.5::NgranBands;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.Cdma2000RegistrationInfo;
+import @1.5::RegStateResult.AccessTechnologySpecificInfo.EutranRegistrationInfo;
+import @1.5::RegistrationFailCause;
+import @1.5::SetupDataCallResult;
+
+import android.hidl.safe_union@1.0::Monostate;
+
+struct QosBandwidth {
+    /** Maximum bit rate possible on the bearer */
+    uint32_t maxBitrateKbps;
+    /** Minimum bit rate that is guaranteed to be provided by the network */
+    uint32_t guaranteedBitrateKbps;
+};
+
+/** LTE/EPS Quality of Service parameters as per 3gpp spec 24.301 sec 9.9.4.3. */
+struct EpsQos {
+    /**
+     * Quality of Service Class Identifier (QCI), see 3GPP TS 23.203 and 29.212.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     */
+    uint16_t qci;
+    QosBandwidth downlink;
+    QosBandwidth uplink;
+};
+
+/** 5G Quality of Service parameters as per 3gpp spec 24.501 sec 9.11.4.12 */
+struct NrQos {
+    /**
+     * 5G QOS Identifier (5QI), see 3GPP TS 24.501 and 23.501.
+     * The allowed values are standard values(1-9, 65-68, 69-70, 75, 79-80, 82-85)
+     * defined in the spec and operator specific values in the range 128-254.
+     */
+    uint16_t fiveQi;
+    QosBandwidth downlink;
+    QosBandwidth uplink;
+    /**
+     * QOS flow identifier of the QOS flow description in the
+     * range of QosFlowIdRange::MIN to QosFlowIdRange::MAX
+     */
+    uint8_t qfi;
+    uint16_t averagingWindowMs;
+};
+
+/** Allowed values for 5G QOS flow identifier */
+enum QosFlowIdRange : uint8_t {
+    MIN = 1,
+    MAX = 63
+};
+
+/** EPS or NR QOS parameters */
+safe_union Qos {
+    Monostate noinit;
+    EpsQos eps;
+    NrQos nr;
+};
+
+/**
+ * Next header protocol numbers defined by IANA, RFC 5237
+ */
+enum QosProtocol : int8_t {
+    /** No protocol specified */
+    UNSPECIFIED = -1,
+    /** Transmission Control Protocol */
+    TCP = 6,
+    /** User Datagram Protocol */
+    UDP = 17,
+    /** Encapsulating Security Payload Protocol */
+    ESP = 50,
+    /** Authentication Header */
+    AH = 51,
+};
+
+enum QosFilterDirection : int8_t {
+    DOWNLINK = 0,
+    UPLINK = 1,
+    BIDIRECTIONAL = 2,
+};
+
+/** Allowed port numbers */
+enum QosPortRange : uint16_t {
+    MIN = 20,
+    MAX = 65535
+};
+
+enum RadioError : @1.0::RadioError {
+    /** 1X voice and SMS are not allowed simulteneously. */
+    SIMULTANEOUS_SMS_AND_CALL_NOT_ALLOWED = 67,
+
+    /** Access is barred. */
+    ACCESS_BARRED = 68,
+
+    /**
+     * SMS is blocked due to call control, e.g., resource unavailable
+     * in the SMR entity.
+     */
+    BLOCKED_DUE_TO_CALL = 69,
+
+    /**
+     * Returned from setRadioPowerResponse when detecting RF HW issues. Some RF
+     * Front-End(RFFE) components like antenna are considered critical for modem
+     * to provide telephony service. This RadioError is used when modem detect
+     * such RFFE problem.
+     */
+    RF_HARDWARE_ISSUE = 70,
+
+    /**
+     * Returned from setRadioPowerResponse when detecting no RF calibration
+     * issue. Unlike RF_HARDWARE_ISSUE, this is a SW problem and no HW repair is
+     * needed.
+     */
+    NO_RF_CALIBRATION_INFO = 71,
+};
+
+/**
+ * Overwritten from @1.0::RadioResponseInfo in order to update the RadioError to 1.6 version.
+ */
+struct RadioResponseInfo {
+    RadioResponseType type;               // Response type
+    int32_t serial;                       // Serial number of the request
+    RadioError error;                     // Response error
+};
+
+/**
+ * Defines range of ports. start and end are the first and last port numbers
+ * (inclusive) in the range. Both start and end are in QosPortRange.MIN to
+ * QosPortRange.MAX range. A single port shall be represented by the same
+ * start and end value.
+ */
+struct PortRange {
+    int32_t start;
+    int32_t end;
+};
+
+/** Port is optional, contains either single port or range of ports */
+safe_union MaybePort {
+    Monostate noinit;
+    PortRange range;
+};
+
+/** See 3gpp 24.008 10.5.6.12 and 3gpp 24.501 9.11.4.13 */
+struct QosFilter {
+    /**
+     * Local and remote IP addresses, typically one IPv4 or one IPv6
+     * or one of each. Addresses could be with optional "/" prefix
+     * length, e.g.,"192.0.1.3" or "192.0.1.11/16 2001:db8::1/64".
+     * If the prefix length is absent the addresses are assumed to be
+     * point to point with IPv4 having a prefix length of 32 and
+     * IPv6 128.
+     */
+    vec<string> localAddresses;
+    vec<string> remoteAddresses;
+
+    /** Local and remote port/ranges */
+    MaybePort localPort;
+    MaybePort remotePort;
+
+    /** QoS protocol */
+    QosProtocol protocol;
+
+    /** Type of service value or mask as defined in RFC 1349 */
+    safe_union TypeOfService {
+         Monostate noinit;
+         uint8_t value;
+    } tos;
+
+    /** IPv6 flow label as defined in RFC 6437 */
+    safe_union Ipv6FlowLabel {
+         Monostate noinit;
+         uint32_t value;
+    } flowLabel;
+
+    /** IPSec security parameter index */
+    safe_union IpsecSpi {
+         Monostate noinit;
+         uint32_t value;
+    } spi;
+
+    /** Filter direction */
+    QosFilterDirection direction;
+
+    /**
+     * Specifies the order in which the filter needs to be matched.
+     * A lower numerical(positive) value has a higher precedence.
+     * Set -1 when unspecified.
+     */
+    int32_t precedence;
+};
+
+/** QOS session associated with a dedicated bearer */
+struct QosSession {
+    /** Unique ID of the QoS session within the data call */
+    int32_t qosSessionId;
+
+    /** QOS attributes */
+    Qos qos;
+
+    /** List of QOS filters associated with this session */
+    vec<QosFilter> qosFilters;
+};
+
+/** The allowed failure modes on an IWLAN handover failure. */
+enum HandoverFailureMode : int8_t {
+    /**
+     * On data handover failure, fallback to the source data transport when the
+     * fail cause is due to a hand off preference change.
+     */
+    LEGACY = 0,
+
+    /** On data handover failure, fallback to the source data transport. */
+    DO_FALLBACK = 1,
+
+    /**
+     * On data handover failure, retry the handover instead of falling back to
+     * the source data transport.
+     */
+    NO_FALLBACK_RETRY_HANDOVER = 2,
+
+    /**
+     * On data handover failure, setup a new data connection by sending a normal
+     * request to the underlying data service.
+     */
+    NO_FALLBACK_RETRY_SETUP_NORMAL = 3
+};
+
+/**
+ * Overwritten from @1.5::SetupDataCallResult in order to change the suggestedRetryTime
+ * to 64-bit value. In the future, this must be extended instead of overwritten.
+ * Also added defaultQos, qosSessions, handoverFailureMode, pduSessionId, sliceInfo,
+ * and traffic descriptors in this version.
+ */
+struct SetupDataCallResult {
+    /** Data call fail cause. DataCallFailCause.NONE if no error. */
+    DataCallFailCause cause;
+
+    /**
+     * If cause is not DataCallFailCause.NONE, this field indicates the network suggested data
+     * retry back-off time in milliseconds. Negative value indicates network does not give any
+     * suggestion. 0 indicates retry should be performed immediately. 0x7fffffffffffffff indicates
+     * the device should not retry data setup anymore.
+     *
+     * During this time, no calls to IRadio@1.6::SetupDataCall for this APN will be made unless
+     * IRadioIndication@1.6::unthrottleApn is sent with the same APN.
+     */
+    int64_t suggestedRetryTime;
+
+    /** Context ID, uniquely identifies this data connection. */
+    int32_t cid;
+
+    /** Data connection active status. */
+    DataConnActiveStatus active;
+
+    /**
+     * PDP protocol type. If cause is DataCallFailCause.ONLY_SINGLE_BEARER_ALLOWED, this is the
+     * protocol type supported, such as "IP" or "IPV6".
+     */
+    PdpProtocolType type;
+
+    /** The network interface name. */
+    string ifname;
+
+    /**
+     * List of link address.
+     */
+    vec<LinkAddress> addresses;
+
+    /**
+     * List of DNS server addresses, e.g., "192.0.1.3" or "192.0.1.11 2001:db8::1". Empty if no dns
+     * server addresses returned.
+     */
+    vec<string> dnses;
+
+    /**
+     * List of default gateway addresses, e.g., "192.0.1.3" or "192.0.1.11 2001:db8::1".
+     * When empty, the addresses represent point to point connections.
+     */
+    vec<string> gateways;
+
+    /**
+     * List of P-CSCF(Proxy Call State Control Function) addresses via PCO(Protocol Configuration
+     * Option), e.g., "2001:db8::1 2001:db8::2 2001:db8::3". Empty if not IMS client.
+     */
+    vec<string> pcscf;
+
+    /**
+     * MTU received from network for IPv4.
+     * Value <= 0 means network has either not sent a value or sent an invalid value.
+     */
+    int32_t mtuV4;
+
+    /**
+     * MTU received from network for IPv6.
+     * Value <= 0 means network has either not sent a value or sent an invalid value.
+     */
+    int32_t mtuV6;
+
+    /** Default bearer QoS. Applicable to LTE and NR */
+    Qos defaultQos;
+
+    /**
+     * Active QOS sessions of the dedicated bearers. Applicable to
+     * PDNs that support dedicated bearers.
+     */
+    vec<QosSession> qosSessions;
+
+    /** Specifies the fallback mode on an IWLAN handover failure. */
+    HandoverFailureMode handoverFailureMode;
+
+    /**
+     * The allocated pdu session id for this data call.
+     * A value of 0 means no pdu session id was attached to this call.
+     *
+     * Reference: 3GPP TS 24.007 section 11.2.3.1b
+     */
+    int32_t pduSessionId;
+
+    /**
+     * Slice used for this data call. It is valid only when this data call is on
+     * AccessNetwork:NGRAN.
+     */
+    OptionalSliceInfo sliceInfo;
+
+    /**
+     * TrafficDescriptors for which this data call must be used. It only includes
+     * the TDs for which a data call has been requested so far; it is not an
+     * exhaustive list.
+     */
+    vec<TrafficDescriptor> trafficDescriptors;
+};
+
+/**
+ * NR Dual connectivity state
+ */
+enum NrDualConnectivityState: int8_t {
+    /**
+     * Enable NR dual connectivity. Enabled state does not mean dual connectivity
+     * is active. It means device is allowed to connect to both primary and secondary.
+     */
+    ENABLE = 1,
+    /**
+     * Disable NR dual connectivity. Disabled state does not mean secondary cell is released.
+     * Modem will release it only if current bearer is released to avoid radio link failure.
+     */
+    DISABLE = 2,
+    /**
+     * Disable NR dual connectivity and force secondary cell to be released if dual connectivity
+     * was active. This may result in radio link failure.
+     */
+    DISABLE_IMMEDIATE= 3,
+};
+
+/**
+ * Overwritten from @1.2::LinkCapacityEstimate to update LinkCapacityEstimate to 1.6 version.
+ */
+struct LinkCapacityEstimate {
+
+   /**
+    * Estimated downlink capacity in kbps. In case of a dual connected network,
+    * this includes capacity of both primary and secondary. This bandwidth estimate shall be
+    * the estimated maximum sustainable link bandwidth (as would be measured
+    * at the Upper PDCP or SNDCP SAP). If the DL Aggregate Maximum Bit Rate is known,
+    * this value shall not exceed the DL-AMBR for the Internet PDN connection.
+    * This must be filled with 0 if network is not connected.
+    */
+   uint32_t downlinkCapacityKbps;
+
+   /**
+    * Estimated uplink capacity in kbps. In case of a dual connected network,
+    * this includes capacity of both primary and secondary. This bandwidth estimate shall be the
+    * estimated maximum sustainable link bandwidth (as would be measured at the
+    * Upper PDCP or SNDCP SAP). If the UL Aggregate Maximum Bit Rate is known,
+    * this value shall not exceed the UL-AMBR for the Internet PDN connection.
+    * This must be filled with 0 if network is not connected.
+    */
+   uint32_t uplinkCapacityKbps;
+
+   /**
+    * Estimated downlink capacity of secondary carrier in a dual connected NR mode in kbps.
+    * This bandwidth estimate shall be the estimated maximum sustainable link bandwidth
+    * (as would be measured at the Upper PDCP or SNDCP SAP). This is valid only
+    * in if device is connected to both primary and secodary in dual connected
+    * mode. This must be filled with 0 if secondary is not connected or if
+    * modem does not support this feature.
+    */
+   uint32_t secondaryDownlinkCapacityKbps;
+
+   /**
+    * Estimated uplink capacity secondary carrier in a dual connected NR mode in kbps.
+    * This bandwidth estimate shall be the estimated
+    * maximum sustainable link bandwidth (as would be measured at the Upper PDCP or SNDCP SAP).
+    * This is valid only in if device is connected to both primary and secodary in dual connected
+    * mode.This must be filled with 0 if secondary is not connected or if modem
+    * does not support this feature.
+    */
+   uint32_t secondaryUplinkCapacityKbps;
+};
+
+enum DataThrottlingAction : int8_t {
+    /* Clear all existing data throttling. */
+    NO_DATA_THROTTLING = 0,
+
+    /**
+     * Enact secondary carrier data throttling and remove any existing data
+     * throttling on anchor carrier.
+     */
+    THROTTLE_SECONDARY_CARRIER = 1,
+
+    /**
+     * Enact anchor carrier data throttling and disable data on secondary
+     * carrier if currently enabled.
+     */
+    THROTTLE_ANCHOR_CARRIER = 2,
+
+    /**
+     * Immediately hold on to current level of throttling.
+     */
+    HOLD = 3
+};
+
+/**
+ * Defines the values for VoPS indicator of NR as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum VopsIndicator : uint8_t {
+    /** IMS voice over PS session not supported */
+    VOPS_NOT_SUPPORTED = 0,
+    /** IMS voice over PS session supported over 3GPP access */
+    VOPS_OVER_3GPP = 1,
+    /** IMS voice over PS session supported over non-3GPP access */
+    VOPS_OVER_NON_3GPP = 2,
+};
+
+/**
+ * Defines the values for emergency service indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmcIndicator : uint8_t {
+    /** Emergency services not supported */
+    EMC_NOT_SUPPORTED = 0,
+    /** Emergency services supported in NR connected to 5GCN only */
+    EMC_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services supported in E-UTRA connected to 5GCN only */
+    EMC_EUTRA_CONNECTED_TO_5GCN = 2,
+    /** Emergency services supported in NR connected to 5GCN and E-UTRA connected to 5GCN */
+    EMC_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Defines the values for emergency service fallback indicator of NR
+ * as per 3gpp spec 24.501 sec 9.10.3.5
+ */
+enum EmfIndicator : uint8_t {
+    /** Emergency services fallback not supported */
+    EMF_NOT_SUPPORTED = 0,
+    /** Emergency services fallback supported in NR connected to 5GCN only */
+    EMF_NR_CONNECTED_TO_5GCN = 1,
+    /** Emergency services fallback supported in E-UTRA connected to 5GCN only */
+    EMF_EUTRA_CONNECTED_TO_5GCN = 2,
+    /**
+     * Emergency services fallback supported in NR connected to 5GCN and E-UTRA
+     * connected to 5GCN.
+     */
+    EMF_BOTH_NR_EUTRA_CONNECTED_TO_5GCN = 3
+};
+
+/**
+ * Type to define the NR specific network capabilities for voice over PS including
+ * emergency and normal voice calls.
+ */
+struct NrVopsInfo {
+    /**
+     * This indicates if the camped network supports VoNR services, and what kind of services
+     * it supports. This information is received from NR network during NR NAS registration
+     * procedure through NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> IMS VoPS
+     */
+    VopsIndicator vopsSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service. This information
+     * is received from NR network through two sources:
+     * a. During NR NAS registration procedure through NR REGISTRATION ACCEPT.
+     *    Refer 3GPP 24.501 EPS 5GS network feature support -> EMC
+     * b. In case the device is not registered on the network.
+     *    Refer 3GPP 38.331 SIB1 : ims-EmergencySupport
+     *    If device is registered on NR, then this field indicates whether the cell
+     *    supports IMS emergency bearer services for UEs in limited service mode.
+     */
+    EmcIndicator emcSupported;
+
+    /**
+     * This indicates if the camped network supports VoNR emergency service fallback. This
+     * information is received from NR network during NR NAS registration procedure through
+     * NR REGISTRATION ACCEPT.
+     * Refer 3GPP 24.501 EPS 5GS network feature support -> EMF
+     */
+    EmfIndicator emfSupported;
+};
+
+struct LteSignalStrength {
+    @1.0::LteSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 136.213 section 7.2.3.
+     *
+     * Range [1, 6], INT_MAX means invalid/unreported.
+     */
+    uint32_t cqiTableIndex;
+};
+
+struct NrSignalStrength {
+    @1.4::NrSignalStrength base;
+
+    /**
+     * CSI channel quality indicator (CQI) table index. There are multiple CQI tables.
+     * The definition of CQI in each table is different.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [1, 3], INT_MAX means invalid/unreported.
+     */
+    uint32_t csiCqiTableIndex;
+
+    /**
+     * CSI channel quality indicator (CQI) for all subbands.
+     *
+     * If the CQI report is for the entire wideband, a single CQI index is provided.
+     * If the CQI report is for all subbands, one CQI index is provided for each subband,
+     * in ascending order of subband index.
+     * If CQI is not available, the CQI report is empty.
+     *
+     * Reference: 3GPP TS 138.214 section 5.2.2.1.
+     *
+     * Range [0, 15], 0xFF means invalid/unreported.
+     */
+    vec<uint8_t> csiCqiReport;
+};
+
+/**
+ * Overwritten from @1.4::SignalStrength in order to update LteSignalStrength and NrSignalStrength.
+ */
+struct SignalStrength {
+    /**
+     * If GSM measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    GsmSignalStrength gsm;
+
+    /**
+     * If CDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    CdmaSignalStrength cdma;
+
+    /**
+     * If EvDO measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    EvdoSignalStrength evdo;
+
+    /**
+     * If LTE measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    LteSignalStrength lte;
+
+    /**
+     * If TD-SCDMA measurements are provided, this structure must contain valid measurements;
+     * otherwise all fields should be set to INT_MAX to mark them as invalid.
+     */
+    TdscdmaSignalStrength tdscdma;
+
+    /**
+     * If WCDMA measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    WcdmaSignalStrength wcdma;
+
+    /**
+     * If NR 5G measurements are provided, this structure must contain valid measurements; otherwise
+     * all fields should be set to INT_MAX to mark them as invalid.
+     */
+    NrSignalStrength nr;
+};
+
+/** Overwritten from @1.5::CellInfoLte in order to update LteSignalStrength. */
+struct CellInfoLte {
+    CellIdentityLte cellIdentityLte;
+    LteSignalStrength signalStrengthLte;
+};
+
+/** Overwritten from @1.5::CellInfoNr in order to update NrSignalStrength. */
+struct CellInfoNr {
+    CellIdentityNr cellIdentityNr;
+    NrSignalStrength signalStrengthNr;
+};
+
+/** Overwritten from @1.5::CellInfo in order to update LteSignalStrength and NrSignalStrength. */
+struct CellInfo {
+    /**
+     * True if this cell is registered false if not registered.
+     */
+    bool registered;
+    /**
+     * Connection status for the cell.
+     */
+    CellConnectionStatus connectionStatus;
+
+    safe_union CellInfoRatSpecificInfo {
+        /**
+         * 3gpp CellInfo types.
+         */
+        CellInfoGsm gsm;
+        CellInfoWcdma wcdma;
+        CellInfoTdscdma tdscdma;
+        CellInfoLte lte;
+        CellInfoNr nr;
+
+        /**
+         * 3gpp2 CellInfo types;
+         */
+        CellInfoCdma cdma;
+    } ratSpecificInfo;
+};
+
+/** Overwritten from @1.5::NetworkScanResult in order to update the CellInfo to 1.6 version. */
+struct NetworkScanResult {
+    /**
+     * The status of the scan.
+     */
+    ScanStatus status;
+
+    /**
+     * The error code of the incremental result.
+     */
+    RadioError error;
+
+    /**
+     * List of network information as CellInfo.
+     */
+    vec<CellInfo> networkInfos;
+};
+
+/**
+ * Overwritten from @1.5::RegStateResult to 1.6 to support NrRegistrationInfo
+ * version.
+ */
+struct RegStateResult {
+    /**
+     * Registration state
+     *
+     * If the RAT is indicated as a GERAN, UTRAN, or CDMA2000 technology, this value reports
+     * registration in the Circuit-switched domain.
+     * If the RAT is indicated as an EUTRAN, NGRAN, or another technology that does not support
+     * circuit-switched services, this value reports registration in the Packet-switched domain.
+     */
+    RegState regState;
+
+    /**
+     * Indicates the available voice radio technology, valid values as defined by RadioTechnology,
+     * except LTE_CA, which is no longer a valid value on 1.5 or above. When the device is on
+     * carrier aggregation, vendor RIL service should properly report multiple PhysicalChannelConfig
+     * elements through IRadio::currentPhysicalChannelConfigs_1_6.
+     */
+    RadioTechnology rat;
+
+    /**
+     * Cause code reported by the network in case registration fails. This will be a mobility
+     * management cause code defined for MM, GMM, MME or equivalent as appropriate for the RAT.
+     */
+    RegistrationFailCause reasonForDenial;
+
+    /** CellIdentity */
+    CellIdentity cellIdentity;
+
+    /**
+     * The most-recent PLMN-ID upon which the UE registered (or attempted to register if a failure
+     * is reported in the reasonForDenial field). This PLMN shall be in standard format consisting
+     * of a 3 digit MCC concatenated with a 2 or 3 digit MNC.
+     */
+    string registeredPlmn;
+
+    /**
+     * Access-technology-specific registration information, such as for CDMA2000.
+     */
+    safe_union AccessTechnologySpecificInfo {
+        Monostate noinit;
+
+        Cdma2000RegistrationInfo cdmaInfo;
+
+        EutranRegistrationInfo eutranInfo;
+
+        /**
+         * Network capabilities for voice over PS services. This info is valid only on NR
+         * network and must be present when the device is camped on NR. VopsInfo must be
+         * empty when the device is not camped on NR.
+         */
+        NrVopsInfo ngranNrVopsInfo;
+
+        /**
+         * True if the dual transfer mode is supported.
+         * Refer to 3GPP TS 44.108 section 3.4.25.3
+         */
+        bool geranDtmSupported;
+
+    } accessTechnologySpecificInfo;
+};
+
+struct Call {
+    @1.2::Call base;
+    /**
+     * Forwarded number. It can set only one forwarded number based on 3GPP rule of the CS.
+     * Reference: 3GPP TS 24.008 section 10.5.4.21b
+     */
+    string forwardedNumber;
+};
+
+struct PhysicalChannelConfig {
+    /** Connection status for cell. Valid values are PRIMARY_SERVING and SECONDARY_SERVING */
+    CellConnectionStatus status;
+
+    /** The radio technology for this physical channel */
+    RadioTechnology rat;
+
+    /** Downlink Absolute Radio Frequency Channel Number */
+    int32_t downlinkChannelNumber;
+
+    /** Uplink Absolute Radio Frequency Channel Number */
+    int32_t uplinkChannelNumber;
+
+    /** Downlink cell bandwidth, in kHz */
+    int32_t cellBandwidthDownlinkKhz;
+
+    /** Uplink cell bandwidth, in kHz */
+    int32_t cellBandwidthUplinkKhz;
+
+    /**
+     * A list of data calls mapped to this physical channel. The context id must match the cid of
+     * @1.5::SetupDataCallResult. An empty list means the physical channel has no data call mapped
+     * to it.
+     */
+    vec<int32_t> contextIds;
+
+    /**
+     * The physical cell identifier for this cell.
+     *
+     * In UTRAN, this value is primary scrambling code. The range is [0, 511].
+     * Reference: 3GPP TS 25.213 section 5.2.2.
+     *
+     * In EUTRAN, this value is physical layer cell identity. The range is [0, 503].
+     * Reference: 3GPP TS 36.211 section 6.11.
+     *
+     * In 5G RAN, this value is physical layer cell identity. The range is [0, 1007].
+     * Reference: 3GPP TS 38.211 section 7.4.2.1.
+     */
+    uint32_t physicalCellId;
+
+    /**
+     * The frequency band to scan.
+     */
+    safe_union Band {
+        /** Valid only if radioAccessNetwork = GERAN. */
+        GeranBands geranBand;
+        /** Valid only if radioAccessNetwork = UTRAN. */
+        UtranBands utranBand;
+        /** Valid only if radioAccessNetwork = EUTRAN. */
+        EutranBands eutranBand;
+        /** Valid only if radioAccessNetwork = NGRAN. */
+        NgranBands ngranBand;
+    } band;
+};
+
+/**
+ * Extended from @1.5 NgranBands
+ * IRadio 1.6 supports NGRAN bands up to V16.5.0
+ */
+enum NgranBands  : @1.5::NgranBands {
+    /** 3GPP TS 38.101-1, Table 5.2-1: FR1 bands */
+    BAND_26 = 26,
+    BAND_46 = 46,
+    BAND_53 = 53,
+    BAND_96 = 96,
+};
+
+/**
+ * This safe_union represents an optional slice info
+ */
+safe_union OptionalSliceInfo {
+    Monostate noinit;
+    SliceInfo value;
+};
+
+/**
+ * This struct represents a S-NSSAI as defined in 3GPP TS 24.501.
+ */
+struct SliceInfo {
+    /**
+     * The type of service provided by the slice.
+     *
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    SliceServiceType sst;
+
+    /**
+     * Slice differentiator is the identifier of a slice that has
+     * SliceServiceType as SST. A value of -1 indicates that there is
+     * no corresponding SliceInfo of the HPLMN.
+     *
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    int32_t sliceDifferentiator;
+
+    /**
+     * This SST corresponds to a SliceInfo (S-NSSAI) of the HPLMN; the SST is
+     * mapped to this value.
+     *
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    SliceServiceType mappedHplmnSst;
+
+    /**
+     * Present only if both sliceDifferentiator and mappedHplmnSst are also
+     * present. This SD corresponds to a SliceInfo (S-NSSAI) of the HPLMN;
+     * sliceDifferentiator is mapped to this value. A value of -1 indicates that
+     * there is no corresponding SliceInfo of the HPLMN.
+     *
+     * see: 3GPP TS 24.501 Section 9.11.2.8.
+     */
+    int32_t mappedHplmnSD;
+
+    /**
+     * Field to indicate the current status of the slice.
+     */
+    SliceStatus status;
+};
+
+/**
+ * Slice/Service Type as defined in 3GPP TS 23.501.
+ */
+enum SliceServiceType : uint8_t {
+    /* Not specified */
+    NONE = 0,
+
+    /* Slice suitable for the handling of 5G enhanced Mobile Broadband */
+    EMBB = 1,
+
+    /**
+     * Slice suitable for the handling of ultra-reliable low latency
+     * communications
+     */
+    URLLC = 2,
+
+    /* Slice suitable for the handling of massive IoT */
+    MIOT = 3,
+};
+
+/**
+ * Expose more setup data call failures.
+ */
+enum DataCallFailCause : @1.4::DataCallFailCause {
+    /**
+     * Data call fail due to the slice not being allowed for the data call.
+     */
+    SLICE_REJECTED = 0x8CC,
+
+    /**
+     * No matching rule available for the request, and match-all rule is not allowed for it.
+     */
+    MATCH_ALL_RULE_NOT_ALLOWED = 0x8CD,
+
+    /**
+     * If connection failed for all matching URSP rules.
+     */
+    ALL_MATCHING_RULES_FAILED = 0x8CE,
+};
+
+/**
+ * This safe_union represents an optional DNN. DNN stands for Data Network Name
+ * and represents an APN as defined in 3GPP TS 23.003.
+ */
+safe_union OptionalDnn {
+    Monostate noinit;
+    string value;
+};
+
+/**
+ * This safe_union represents an optional OsAppId.
+ */
+safe_union OptionalOsAppId {
+    Monostate noinit;
+    OsAppId value;
+};
+
+/**
+ * This safe_union represents an optional TrafficDescriptor.
+ */
+safe_union OptionalTrafficDescriptor {
+    Monostate noinit;
+    TrafficDescriptor value;
+};
+
+/**
+ * This struct represents a traffic descriptor. A valid struct must have at least
+ * one of the optional values present. This is based on the definition of traffic
+ * descriptor in TS 24.526 Section 5.2.
+ */
+struct TrafficDescriptor {
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in
+     * 3GPP TS 23.003.
+     */
+    OptionalDnn dnn;
+    /**
+     * Indicates the OsId + OsAppId (used as category in Android).
+     */
+    OptionalOsAppId osAppId;
+};
+
+/**
+ * This struct represents the OsId + OsAppId as defined in TS 24.526 Section 5.2
+ */
+struct OsAppId {
+    /**
+     * Byte array representing OsId + OsAppId. The minimum length of the array is
+     * 18 and maximum length is 272 (16 bytes for OsId + 1 byte for OsAppId length
+     * + up to 255 bytes for OsAppId).
+     */
+    vec<uint8_t> osAppId;
+};
+
+/**
+ * This struct represents the current slicing configuration.
+ */
+struct SlicingConfig {
+    /**
+     * This vector contains the current URSP rules. Empty vector represents that no
+     * rules are configured.
+     */
+    vec<UrspRule> urspRules;
+    /**
+     * List of all slices.
+     */
+    vec<SliceInfo> sliceInfo;
+};
+
+/**
+ * This struct represents a single URSP rule as defined in 3GPP TS 24.526.
+ */
+struct UrspRule {
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower
+     * precedence.
+     */
+    uint8_t precedence;
+    /**
+     * Used as a matcher for network requests.
+     */
+    vec<TrafficDescriptor> trafficDescriptors;
+    /**
+     * List of routes (connection parameters) that must be used for requests
+     * matching a trafficDescriptor.
+     */
+    vec<RouteSelectionDescriptor> routeSelectionDescriptor;
+};
+
+/**
+ * This struct represents a single route selection descriptor as defined in
+ * 3GPP TS 24.526.
+ */
+struct RouteSelectionDescriptor {
+    /**
+     * Precedence value in the range of 0 to 255. Higher value has lower
+     * precedence.
+     */
+    uint8_t precedence;
+    /**
+     * Parameters defining this RouteSelectionDescriptor. The length of the vector
+     * must be >= 1.
+     */
+    vec<RouteSelectionDescriptorParams> routeSelectionDescriptorParams;
+};
+
+/**
+ * This struct represents a route selection descriptor. A valid struct must have
+ * at least one of the vectors non-empty.
+ */
+struct RouteSelectionDescriptorParams {
+    /**
+     * Valid values are IP, IPV6 and IPV4V6.
+     */
+    OptionalPdpProtocolType sessionType;
+    OptionalSscMode sscMode;
+    /**
+     * There can be 0 or more SliceInfo specified in a route descriptor.
+     */
+    vec<SliceInfo> sliceInfo;
+    /**
+     * DNN stands for Data Network Name and represents an APN as defined in
+     * 3GPP TS 23.003. There can be 0 or more DNNs specified in a route
+     * descriptor.
+     */
+    vec<string> dnn;
+};
+
+/**
+ * This safe_union represents an optional PdpProtocolType.
+ */
+safe_union OptionalPdpProtocolType {
+    Monostate noinit;
+    PdpProtocolType value;
+};
+
+/**
+ * This safe_union represents an optional SscMode.
+ */
+safe_union OptionalSscMode {
+    Monostate noinit;
+    SscMode value;
+};
+
+enum SliceStatus : int8_t {
+    UNKNOWN,
+    CONFIGURED, // Configured but not allowed or rejected yet
+    ALLOWED,    // Allowed to be used
+    REJECTED_NOT_AVAILABLE_IN_PLMN,     // Rejected because not available in PLMN
+    REJECTED_NOT_AVAILABLE_IN_REG_AREA, // Rejected because not available in reg area
+    DEFAULT_CONFIGURED,     // Considered valid when configured/allowed slices are not available
+};
+
+/**
+ * Enum representing session and service continuity mode as defined in
+ * 3GPP TS 23.501.
+ */
+enum SscMode : int8_t {
+    MODE_1 = 1,
+    MODE_2 = 2,
+    MODE_3 = 3,
+};
+
+/**
+ * Public key type from carrier certificate.
+ */
+enum PublicKeyType : int8_t {
+    EPDG    = 1,                   // Key type to be used for ePDG
+    WLAN    = 2,                   // Key type to be used for WLAN
+};
+
+/**
+ * Carrier specific Information sent by the carrier,
+ * which will be used to encrypt the IMSI and IMPI.
+ */
+struct ImsiEncryptionInfo {
+    @1.1::ImsiEncryptionInfo base;
+    PublicKeyType keyType;         // Public key type
+};
+
+/**
+ * Phonebook-record-information specified by EF_ADN(Abbreviated dialing numbers)
+ * record of SIM as per 3GPP spec 31.102 v15 Section-4.4.2.3.
+ */
+struct PhonebookRecordInfo {
+    /** Record index. 0 is used to insert a record */
+    uint32_t recordId;
+
+    /** Alpha identifier, empty string if no value */
+    string name;
+
+    /** Dialling number, empty string if no value */
+    string number;
+
+    /** Email addresses */
+    vec<string> emails;
+
+    /** Additional numbers */
+    vec<string> additionalNumbers;
+};
+
+struct PhonebookCapacity {
+    /**
+     * Maximum number of ADN records possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxAdnRecords;
+
+    /**
+     * Used ADN records in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t usedAdnRecords;
+
+    /**
+     * Maximum email records possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxEmailRecords;
+
+    /**
+     * Used email records in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t usedEmailRecords;
+
+    /**
+     * Maximum additional number records possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxAdditionalNumberRecords;
+
+    /**
+     * Used additional number records in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t usedAdditionalNumberRecords;
+
+    /**
+     * Maximum name length possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxNameLen;
+
+    /**
+     * Maximum number length possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxNumberLen;
+
+    /**
+     * Maximum email length possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxEmailLen;
+
+    /**
+     * Maximum additional number length possible in the SIM phonebook
+     * Needs to be non-negative
+     */
+    int32_t maxAdditionalNumberLen;
+};
+
+/**
+ * Enum representing the status of the received PB indication,
+ * PB_RECEIVED_OK indicates this retrieval is fine
+ * PB_RECEIVED_ERROR indicates one error happens, in general, the process
+ *   can't be restored soon.
+ * PB_RECEIVED_ABORT indicates the process is interrupted, in this case,
+ *   modem might need resources and interrupt the current process, or it is
+ *   timed out to receive all indications, and client can retry soon.
+ * PB_RECEIVED_FINAL indicates the whole process is finished with a full
+ *   chunk of phonebook data, means this is a last indication with the left
+ *   data.
+ */
+enum PbReceivedStatus : int8_t {
+    PB_RECEIVED_OK = 1,
+    PB_RECEIVED_ERROR = 2,
+    PB_RECEIVED_ABORT = 3,
+    PB_RECEIVED_FINAL = 4,
+};
+
diff --git a/radio/1.6/vts/OWNERS b/radio/1.6/vts/OWNERS
new file mode 100644
index 0000000..a07c917
--- /dev/null
+++ b/radio/1.6/vts/OWNERS
@@ -0,0 +1 @@
+include ../../1.0/vts/OWNERS
diff --git a/radio/1.6/vts/functional/Android.bp b/radio/1.6/vts/functional/Android.bp
new file mode 100644
index 0000000..2bc6af3
--- /dev/null
+++ b/radio/1.6/vts/functional/Android.bp
@@ -0,0 +1,61 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalRadioV1_6TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "radio_hidl_hal_api.cpp",
+        "radio_hidl_hal_misc.cpp",
+        "radio_hidl_hal_test.cpp",
+        "radio_response.cpp",
+        "radio_indication.cpp",
+        "VtsHalRadioV1_6TargetTest.cpp",
+        "vts_test_util_v1_6.cpp",
+    ],
+    static_libs: [
+        "RadioVtsTestUtilBase",
+        "RadioConfigVtsTestResponse",
+        "android.hardware.radio@1.6",
+        "android.hardware.radio@1.5",
+        "android.hardware.radio@1.4",
+        "android.hardware.radio@1.3",
+        "android.hardware.radio@1.2",
+        "android.hardware.radio@1.1",
+        "android.hardware.radio@1.0",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio.config@1.3",
+    ],
+    header_libs: [
+        "radio.util.header@1.0",
+        "radio.config.util.header@1.3",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/radio/1.6/vts/functional/VtsHalRadioV1_6TargetTest.cpp b/radio/1.6/vts/functional/VtsHalRadioV1_6TargetTest.cpp
new file mode 100644
index 0000000..ffa3587
--- /dev/null
+++ b/radio/1.6/vts/functional/VtsHalRadioV1_6TargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 <radio_hidl_hal_utils_v1_6.h>
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioHidlTest_v1_6);
+INSTANTIATE_TEST_SUITE_P(PerInstance, RadioHidlTest_v1_6,
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 android::hardware::radio::V1_6::IRadio::descriptor)),
+                         android::hardware::PrintInstanceNameToString);
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_api.cpp b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
new file mode 100644
index 0000000..391166b
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_api.cpp
@@ -0,0 +1,966 @@
+/*
+ * 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 <radio_hidl_hal_utils_v1_6.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadio.setAllowedNetworkTypesBitmap for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setAllowedNetworkTypesBitmap) {
+    serial = GetRandomSerialNumber();
+    ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_4::RadioAccessFamily>
+            allowedNetworkTypesBitmap{};
+    allowedNetworkTypesBitmap |= ::android::hardware::radio::V1_4::RadioAccessFamily::LTE;
+
+    radio_v1_6->setAllowedNetworkTypesBitmap(serial, allowedNetworkTypesBitmap);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::OPERATION_NOT_ALLOWED,
+                 ::android::hardware::radio::V1_6::RadioError::MODE_NOT_SUPPORTED,
+                 ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                 ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::NO_RESOURCES}));
+    }
+}
+
+/*
+ * Test IRadio.getAllowedNetworkTypesBitmap for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getAllowedNetworkTypesBitmap) {
+    serial = GetRandomSerialNumber();
+    ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_4::RadioAccessFamily>
+            allowedNetworkTypesBitmap{};
+    allowedNetworkTypesBitmap |= ::android::hardware::radio::V1_4::RadioAccessFamily::LTE;
+
+    radio_v1_6->setAllowedNetworkTypesBitmap(serial, allowedNetworkTypesBitmap);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        sleep(3);  // wait for modem
+        serial = GetRandomSerialNumber();
+        radio_v1_6->getAllowedNetworkTypesBitmap(serial);
+
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+        EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+        if (getRadioHalCapabilities()) {
+            ASSERT_TRUE(CheckAnyOfErrors(
+                    radioRsp_v1_6->rspInfo.error,
+                    {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+        } else {
+            ASSERT_TRUE(CheckAnyOfErrors(
+                    radioRsp_v1_6->rspInfo.error,
+                    {::android::hardware::radio::V1_6::RadioError::NONE,
+                     ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                     ::android::hardware::radio::V1_6::RadioError::OPERATION_NOT_ALLOWED,
+                     ::android::hardware::radio::V1_6::RadioError::MODE_NOT_SUPPORTED,
+                     ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+                     ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                     ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                     ::android::hardware::radio::V1_6::RadioError::NO_RESOURCES}));
+        }
+    }
+}
+
+/*
+ * Test IRadio.setupDataCall_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setupDataCall_1_6) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_5::AccessNetwork accessNetwork =
+            ::android::hardware::radio::V1_5::AccessNetwork::EUTRAN;
+
+    android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
+    memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
+    dataProfileInfo.profileId = DataProfileId::DEFAULT;
+    dataProfileInfo.apn = hidl_string("internet");
+    dataProfileInfo.protocol = PdpProtocolType::IP;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IP;
+    dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+    dataProfileInfo.user = hidl_string("username");
+    dataProfileInfo.password = hidl_string("password");
+    dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+    dataProfileInfo.maxConnsTime = 300;
+    dataProfileInfo.maxConns = 20;
+    dataProfileInfo.waitTime = 0;
+    dataProfileInfo.enabled = true;
+    dataProfileInfo.supportedApnTypesBitmap = 320;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtuV4 = 0;
+    dataProfileInfo.mtuV6 = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = false;
+
+    bool roamingAllowed = false;
+
+    std::vector<::android::hardware::radio::V1_5::LinkAddress> addresses = {};
+    std::vector<hidl_string> dnses = {};
+
+    ::android::hardware::radio::V1_2::DataRequestReason reason =
+            ::android::hardware::radio::V1_2::DataRequestReason::NORMAL;
+
+    ::android::hardware::radio::V1_6::OptionalSliceInfo optionalSliceInfo;
+    memset(&optionalSliceInfo, 0, sizeof(optionalSliceInfo));
+
+    ::android::hardware::radio::V1_6::OptionalTrafficDescriptor optionalTrafficDescriptor;
+    memset(&optionalTrafficDescriptor, 0, sizeof(optionalTrafficDescriptor));
+
+    bool matchAllRuleAllowed = true;
+
+    Return<void> res =
+            radio_v1_6->setupDataCall_1_6(serial, accessNetwork, dataProfileInfo, roamingAllowed,
+                                          reason, addresses, dnses, -1, optionalSliceInfo,
+                                          optionalTrafficDescriptor, matchAllRuleAllowed);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::SIM_ABSENT,
+                 ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+    } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+    }
+}
+
+TEST_P(RadioHidlTest_v1_6, setupDataCall_1_6_osAppId) {
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_5::AccessNetwork accessNetwork =
+            ::android::hardware::radio::V1_5::AccessNetwork::EUTRAN;
+
+    android::hardware::radio::V1_5::DataProfileInfo dataProfileInfo;
+    memset(&dataProfileInfo, 0, sizeof(dataProfileInfo));
+    dataProfileInfo.profileId = DataProfileId::DEFAULT;
+    dataProfileInfo.apn = hidl_string("internet");
+    dataProfileInfo.protocol = PdpProtocolType::IP;
+    dataProfileInfo.roamingProtocol = PdpProtocolType::IP;
+    dataProfileInfo.authType = ApnAuthType::NO_PAP_NO_CHAP;
+    dataProfileInfo.user = hidl_string("username");
+    dataProfileInfo.password = hidl_string("password");
+    dataProfileInfo.type = DataProfileInfoType::THREE_GPP;
+    dataProfileInfo.maxConnsTime = 300;
+    dataProfileInfo.maxConns = 20;
+    dataProfileInfo.waitTime = 0;
+    dataProfileInfo.enabled = true;
+    dataProfileInfo.supportedApnTypesBitmap = 320;
+    dataProfileInfo.bearerBitmap = 161543;
+    dataProfileInfo.mtuV4 = 0;
+    dataProfileInfo.mtuV6 = 0;
+    dataProfileInfo.preferred = true;
+    dataProfileInfo.persistent = false;
+
+    bool roamingAllowed = false;
+
+    std::vector<::android::hardware::radio::V1_5::LinkAddress> addresses = {};
+    std::vector<hidl_string> dnses = {};
+
+    ::android::hardware::radio::V1_2::DataRequestReason reason =
+            ::android::hardware::radio::V1_2::DataRequestReason::NORMAL;
+
+    ::android::hardware::radio::V1_6::OptionalSliceInfo optionalSliceInfo;
+    memset(&optionalSliceInfo, 0, sizeof(optionalSliceInfo));
+
+    ::android::hardware::radio::V1_6::OptionalTrafficDescriptor optionalTrafficDescriptor;
+    memset(&optionalTrafficDescriptor, 0, sizeof(optionalTrafficDescriptor));
+
+    ::android::hardware::radio::V1_6::TrafficDescriptor trafficDescriptor;
+    ::android::hardware::radio::V1_6::OsAppId osAppId;
+    osAppId.osAppId = 1;
+    trafficDescriptor.osAppId.value(osAppId);
+    optionalTrafficDescriptor.value(trafficDescriptor);
+
+    bool matchAllRuleAllowed = true;
+
+    Return<void> res =
+            radio_v1_6->setupDataCall_1_6(serial, accessNetwork, dataProfileInfo, roamingAllowed,
+                                          reason, addresses, dnses, -1, optionalSliceInfo,
+                                          optionalTrafficDescriptor, matchAllRuleAllowed);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::SIM_ABSENT,
+                 ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+    } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::OP_NOT_ALLOWED_BEFORE_REG_TO_NW}));
+        EXPECT_EQ(optionalTrafficDescriptor.value().osAppId.value().osAppId,
+                radioRsp_v1_6->setupDataCallResult.trafficDescriptors[0].osAppId.value().osAppId);
+    }
+}
+
+/*
+ * Test IRadio.getSlicingConfig() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getSlicingConfig) {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getSlicingConfig(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+}
+
+/*
+ * Test IRadio_1_6.sendSms() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, sendSms_1_6) {
+    LOG(DEBUG) << "sendSms";
+    serial = GetRandomSerialNumber();
+    GsmSmsMessage msg;
+    msg.smscPdu = "";
+    msg.pdu = "01000b916105770203f3000006d4f29c3e9b01";
+
+    radio_v1_6->sendSms(serial, msg);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
+             ::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
+            CHECK_GENERAL_ERROR));
+        EXPECT_EQ(0, radioRsp_v1_6->sendSmsResult.errorCode);
+    }
+    LOG(DEBUG) << "sendSms finished";
+}
+
+/*
+ * Test IRadio_1_6.sendSmsExpectMore() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, sendSmsExpectMore_1_6) {
+    LOG(DEBUG) << "sendSmsExpectMore";
+    serial = GetRandomSerialNumber();
+    GsmSmsMessage msg;
+    msg.smscPdu = "";
+    msg.pdu = "01000b916105770203f3000006d4f29c3e9b01";
+
+    radio_v1_6->sendSMSExpectMore(serial, msg);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
+             ::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
+            CHECK_GENERAL_ERROR));
+    }
+    LOG(DEBUG) << "sendSmsExpectMore finished";
+}
+
+/*
+ * Test IRadio_1_6.sendCdmaSms() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, sendCdmaSms_1_6) {
+    LOG(DEBUG) << "sendCdmaSms";
+    serial = GetRandomSerialNumber();
+
+    // Create a CdmaSmsAddress
+    CdmaSmsAddress cdmaSmsAddress;
+    cdmaSmsAddress.digitMode = CdmaSmsDigitMode::FOUR_BIT;
+    cdmaSmsAddress.numberMode = CdmaSmsNumberMode::NOT_DATA_NETWORK;
+    cdmaSmsAddress.numberType = CdmaSmsNumberType::UNKNOWN;
+    cdmaSmsAddress.numberPlan = CdmaSmsNumberPlan::UNKNOWN;
+    cdmaSmsAddress.digits = (std::vector<uint8_t>){11, 1, 6, 5, 10, 7, 7, 2, 10, 3, 10, 3};
+
+    // Create a CdmaSmsSubAddress
+    CdmaSmsSubaddress cdmaSmsSubaddress;
+    cdmaSmsSubaddress.subaddressType = CdmaSmsSubaddressType::NSAP;
+    cdmaSmsSubaddress.odd = false;
+    cdmaSmsSubaddress.digits = (std::vector<uint8_t>){};
+
+    // Create a CdmaSmsMessage
+    android::hardware::radio::V1_0::CdmaSmsMessage cdmaSmsMessage;
+    cdmaSmsMessage.teleserviceId = 4098;
+    cdmaSmsMessage.isServicePresent = false;
+    cdmaSmsMessage.serviceCategory = 0;
+    cdmaSmsMessage.address = cdmaSmsAddress;
+    cdmaSmsMessage.subAddress = cdmaSmsSubaddress;
+    cdmaSmsMessage.bearerData =
+        (std::vector<uint8_t>){15, 0, 3, 32, 3, 16, 1, 8, 16, 53, 76, 68, 6, 51, 106, 0};
+
+    radio_v1_6->sendCdmaSms(serial, cdmaSmsMessage);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
+             ::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
+            CHECK_GENERAL_ERROR));
+    }
+    LOG(DEBUG) << "sendCdmaSms finished";
+}
+
+/*
+ * Test IRadio_1_6.sendCdmaSmsExpectMore() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, sendCdmaSmsExpectMore_1_6) {
+    serial = GetRandomSerialNumber();
+
+    // Create a CdmaSmsAddress
+    CdmaSmsAddress cdmaSmsAddress;
+    cdmaSmsAddress.digitMode = CdmaSmsDigitMode::FOUR_BIT;
+    cdmaSmsAddress.numberMode = CdmaSmsNumberMode::NOT_DATA_NETWORK;
+    cdmaSmsAddress.numberType = CdmaSmsNumberType::UNKNOWN;
+    cdmaSmsAddress.numberPlan = CdmaSmsNumberPlan::UNKNOWN;
+    cdmaSmsAddress.digits = (std::vector<uint8_t>){11, 1, 6, 5, 10, 7, 7, 2, 10, 3, 10, 3};
+
+    // Create a CdmaSmsSubAddress
+    CdmaSmsSubaddress cdmaSmsSubaddress;
+    cdmaSmsSubaddress.subaddressType = CdmaSmsSubaddressType::NSAP;
+    cdmaSmsSubaddress.odd = false;
+    cdmaSmsSubaddress.digits = (std::vector<uint8_t>){};
+
+    // Create a CdmaSmsMessage
+    android::hardware::radio::V1_0::CdmaSmsMessage cdmaSmsMessage;
+    cdmaSmsMessage.teleserviceId = 4098;
+    cdmaSmsMessage.isServicePresent = false;
+    cdmaSmsMessage.serviceCategory = 0;
+    cdmaSmsMessage.address = cdmaSmsAddress;
+    cdmaSmsMessage.subAddress = cdmaSmsSubaddress;
+    cdmaSmsMessage.bearerData =
+            (std::vector<uint8_t>){15, 0, 3, 32, 3, 16, 1, 8, 16, 53, 76, 68, 6, 51, 106, 0};
+
+    radio_v1_6->sendCdmaSmsExpectMore(serial, cdmaSmsMessage);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
+                 ::android::hardware::radio::V1_6::RadioError::SIM_ABSENT},
+                CHECK_GENERAL_ERROR));
+    }
+}
+
+/*
+ * Test IRadio.setRadioPower_1_6() for the response returned by
+ * IRadio.setRadioPowerResponse_1_6().
+ */
+TEST_P(RadioHidlTest_v1_6, setRadioPower_1_6_emergencyCall_cancelled) {
+    // Set radio power to off.
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setRadioPower_1_6(serial, false, false, false);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+
+    // Set radio power to on with forEmergencyCall being true. This should put modem to only scan
+    // emergency call bands.
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setRadioPower_1_6(serial, true, true, true);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+
+    // Set radio power to on with forEmergencyCall being false. This should put modem in regular
+    // operation modem.
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setRadioPower_1_6(serial, true, false, false);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+}
+
+/*
+ * Test IRadio.setNrDualConnectivityState() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setNrDualConnectivityState) {
+    serial = GetRandomSerialNumber();
+
+    Return<void> res =
+            radio_v1_6->setNrDualConnectivityState(serial, NrDualConnectivityState::DISABLE);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                                 {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                                  ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+                                  ::android::hardware::radio::V1_6::RadioError::INVALID_STATE,
+                                  ::android::hardware::radio::V1_6::RadioError::NONE}));
+    }
+}
+
+/*
+ * Test IRadio.isNrDualConnectivityEnabled() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, isNrDualConnectivityEnabled) {
+    serial = GetRandomSerialNumber();
+
+    Return<void> res = radio_v1_6->isNrDualConnectivityEnabled(serial);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(
+                CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                                 {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                                  ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+                                  ::android::hardware::radio::V1_6::RadioError::NONE}));
+    }
+}
+
+/*
+ * Test IRadio.setDataThrottling() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setDataThrottling) {
+    serial = GetRandomSerialNumber();
+
+    Return<void> res = radio_v1_6->setDataThrottling(
+            serial, DataThrottlingAction::THROTTLE_SECONDARY_CARRIER, 60000);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+    }
+
+    sleep(1);
+    serial = GetRandomSerialNumber();
+
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::THROTTLE_ANCHOR_CARRIER,
+                                        60000);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+    }
+
+    sleep(1);
+    serial = GetRandomSerialNumber();
+
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::HOLD, 60000);
+    ASSERT_OK(res);
+
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+    }
+
+    sleep(1);
+    serial = GetRandomSerialNumber();
+
+    res = radio_v1_6->setDataThrottling(serial, DataThrottlingAction::NO_DATA_THROTTLING, 60000);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (getRadioHalCapabilities()) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    } else {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                 ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS}));
+    }
+
+    sleep(1);
+}
+
+/*
+ * Test IRadio.setSimCardPower_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setSimCardPower_1_6) {
+    /* Test setSimCardPower power down */
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_DOWN);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                                 {::android::hardware::radio::V1_6::RadioError::NONE,
+                                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                                  ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                                  ::android::hardware::radio::V1_6::RadioError::SIM_ERR}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled, and should not trigger
+    // CardState::ABSENT when turning off power
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        /* Wait some time for setting sim power down and then verify it */
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+        // applications should be an empty vector of AppStatus
+        EXPECT_EQ(0, cardStatus.applications.size());
+    }
+
+    /* Test setSimCardPower power up */
+    serial = GetRandomSerialNumber();
+    radio_v1_6->setSimCardPower_1_6(serial, CardPowerState::POWER_UP);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    ASSERT_TRUE(CheckAnyOfErrors(radioRsp_v1_6->rspInfo.error,
+                                 {::android::hardware::radio::V1_6::RadioError::NONE,
+                                  ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+                                  ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+                                  ::android::hardware::radio::V1_6::RadioError::SIM_ERR}));
+
+    // setSimCardPower_1_6 does not return  until the request is handled. Just verify that we still
+    // have CardState::PRESENT after turning the power back on
+    if (radioRsp_v1_6->rspInfo.error == ::android::hardware::radio::V1_6::RadioError::NONE) {
+        updateSimCardStatus();
+        EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+    }
+}
+
+/*
+ * Test IRadio.emergencyDial() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6) {
+    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+        ALOGI("Skipping emergencyDial because voice call is not supported in device");
+        return;
+    } else {
+        ALOGI("Running emergencyDial because voice call is supported in device");
+    }
+
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories = static_cast<int>(
+            ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED);
+    std::vector<hidl_string> urns = {""};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN;
+
+    Return<void> res =
+            radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+
+    ALOGI("emergencyDial, rspInfo_v1_0.error = %s\n",
+          toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str());
+
+    ::android::hardware::radio::V1_0::RadioError rspEmergencyDial =
+            radioRsp_v1_6->rspInfo_v1_0.error;
+    // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE
+    // or Emergency_Only.
+    if (isDsDsEnabled() || isTsTsEnabled()) {
+        serial = GetRandomSerialNumber();
+        radio_v1_6->getVoiceRegistrationState_1_6(serial);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) ||
+            isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) {
+            EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+        }
+    } else {
+        EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+    }
+
+    // Give some time for modem to establish the emergency call channel.
+    sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+    // Disconnect all the potential established calls to prevent them affecting other tests.
+    clearPotentialEstablishedCalls();
+}
+
+/*
+ * Test IRadio.emergencyDial() with specified service and its response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6_withServices) {
+    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+        ALOGI("Skipping emergencyDial because voice call is not supported in device");
+        return;
+    } else {
+        ALOGI("Running emergencyDial because voice call is supported in device");
+    }
+
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories =
+            static_cast<int>(::android::hardware::radio::V1_4::EmergencyServiceCategory::AMBULANCE);
+    std::vector<hidl_string> urns = {"urn:service:sos.ambulance"};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::UNKNOWN;
+
+    Return<void> res =
+            radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+
+    ALOGI("emergencyDial_withServices, rspInfo_v1_0.error = %s\n",
+          toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str());
+    ::android::hardware::radio::V1_0::RadioError rspEmergencyDial =
+            radioRsp_v1_6->rspInfo_v1_0.error;
+
+    // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE
+    // or Emergency_Only.
+    if (isDsDsEnabled() || isTsTsEnabled()) {
+        serial = GetRandomSerialNumber();
+        radio_v1_6->getVoiceRegistrationState_1_6(serial);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) ||
+            isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) {
+            EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+        }
+    } else {
+        EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+    }
+    // Give some time for modem to establish the emergency call channel.
+    sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+    // Disconnect all the potential established calls to prevent them affecting other tests.
+    clearPotentialEstablishedCalls();
+}
+
+/*
+ * Test IRadio.emergencyDial() with known emergency call routing and its response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, emergencyDial_1_6_withEmergencyRouting) {
+    if (!deviceSupportsFeature(FEATURE_VOICE_CALL)) {
+        ALOGI("Skipping emergencyDial because voice call is not supported in device");
+        return;
+    } else {
+        ALOGI("Running emergencyDial because voice call is supported in device");
+    }
+
+    serial = GetRandomSerialNumber();
+
+    ::android::hardware::radio::V1_0::Dial dialInfo;
+    dialInfo.address = hidl_string("911");
+    int categories = static_cast<int>(
+            ::android::hardware::radio::V1_4::EmergencyServiceCategory::UNSPECIFIED);
+    std::vector<hidl_string> urns = {""};
+    ::android::hardware::radio::V1_4::EmergencyCallRouting routing =
+            ::android::hardware::radio::V1_4::EmergencyCallRouting::EMERGENCY;
+
+    Return<void> res =
+            radio_v1_6->emergencyDial_1_6(serial, dialInfo, categories, urns, routing, true, true);
+    ASSERT_OK(res);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+
+    ALOGI("emergencyDial_withEmergencyRouting, rspInfo_v1_0.error = %s\n",
+          toString(radioRsp_v1_6->rspInfo_v1_0.error).c_str());
+    ::android::hardware::radio::V1_0::RadioError rspEmergencyDial =
+            radioRsp_v1_6->rspInfo_v1_0.error;
+
+    // In DSDS or TSTS, we only check the result if the current slot is IN_SERVICE
+    // or Emergency_Only.
+    if (isDsDsEnabled() || isTsTsEnabled()) {
+        serial = GetRandomSerialNumber();
+        radio_v1_6->getVoiceRegistrationState_1_6(serial);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        if (isVoiceEmergencyOnly(radioRsp_v1_6->voiceRegResp.regState) ||
+            isVoiceInService(radioRsp_v1_6->voiceRegResp.regState)) {
+            EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+        }
+    } else {
+        EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE, rspEmergencyDial);
+    }
+
+    // Give some time for modem to establish the emergency call channel.
+    sleep(MODEM_EMERGENCY_CALL_ESTABLISH_TIME);
+
+    // Disconnect all the potential established calls to prevent them affecting other tests.
+    clearPotentialEstablishedCalls();
+}
+
+/*
+ * Test IRadio.getCurrentCalls_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getCurrentCalls_1_6) {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getCurrentCalls_1_6(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+}
+
+/*
+ * Test IRadio.setCarrierInfoForImsiEncryption_1_6() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, setCarrierInfoForImsiEncryption_1_6) {
+    serial = GetRandomSerialNumber();
+    ::android::hardware::radio::V1_6::ImsiEncryptionInfo imsiInfo;
+    imsiInfo.base.mcc = "310";
+    imsiInfo.base.mnc = "004";
+    imsiInfo.base.carrierKey = (std::vector<uint8_t>){1, 2, 3, 4, 5, 6};
+    imsiInfo.base.keyIdentifier = "Test";
+    imsiInfo.base.expirationTime = 20180101;
+    imsiInfo.keyType = PublicKeyType::EPDG;
+
+    radio_v1_6->setCarrierInfoForImsiEncryption_1_6(serial, imsiInfo);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo.error,
+                {::android::hardware::radio::V1_6::RadioError::NONE,
+                 ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED}));
+    }
+}
+
+/*
+ * Test IRadio.getSimPhonebookRecords() for the response returned.
+ */
+TEST_F(RadioHidlTest_v1_6, getSimPhonebookRecords) {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getSimPhonebookRecords(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+             ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+             ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+    } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+    }
+}
+
+/*
+ * Test IRadio.getSimPhonebookCapacity for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getSimPhonebookCapacity) {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getSimPhonebookCapacity(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+             ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+             ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+    } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+            ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+            CHECK_GENERAL_ERROR));
+
+        ::android::hardware::radio::V1_6::PhonebookCapacity pbCapacity =
+             radioRsp_v1_6->capacity;
+        if(pbCapacity.maxAdnRecords > 0) {
+            EXPECT_TRUE(pbCapacity.maxNameLen > 0 && pbCapacity.maxNumberLen > 0);
+            EXPECT_TRUE(pbCapacity.usedAdnRecords <= pbCapacity.maxAdnRecords);
+        }
+
+        if(pbCapacity.maxEmailRecords > 0) {
+            EXPECT_TRUE(pbCapacity.maxEmailLen > 0);
+            EXPECT_TRUE(pbCapacity.usedEmailRecords <= pbCapacity.maxEmailRecords);
+        }
+
+        if(pbCapacity.maxAdditionalNumberRecords > 0) {
+            EXPECT_TRUE(pbCapacity.maxAdditionalNumberLen > 0);
+            EXPECT_TRUE(pbCapacity.usedAdditionalNumberRecords <= pbCapacity.maxAdditionalNumberRecords);
+        }
+    }
+}
+
+/*
+ * Test IRadio.updateSimPhonebookRecords() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, updateSimPhonebookRecords) {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getSimPhonebookCapacity(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::INVALID_SIM_STATE,
+             ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+             ::android::hardware::radio::V1_6::RadioError::MODEM_ERR,
+             ::android::hardware::radio::V1_6::RadioError::INVALID_ARGUMENTS,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+    } else if (cardStatus.base.base.base.cardState == CardState::PRESENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+        ::android::hardware::radio::V1_6::PhonebookCapacity pbCapacity =
+                radioRsp_v1_6->capacity;
+
+        serial = GetRandomSerialNumber();
+        radio_v1_6->getSimPhonebookRecords(serial);
+
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+        EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+        ASSERT_TRUE(CheckAnyOfErrors(
+            radioRsp_v1_6->rspInfo.error,
+            {::android::hardware::radio::V1_6::RadioError::NONE,
+             ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED},
+             CHECK_GENERAL_ERROR));
+
+        if(pbCapacity.maxAdnRecords > 0
+                && pbCapacity.usedAdnRecords < pbCapacity.maxAdnRecords) {
+            // Add a phonebook record
+            PhonebookRecordInfo recordInfo;
+            recordInfo.recordId = 0;
+            recordInfo.name = "ABC";
+            recordInfo.number = "1234567890";
+            serial = GetRandomSerialNumber();
+            radio_v1_6->updateSimPhonebookRecords(serial, recordInfo);
+
+            EXPECT_EQ(std::cv_status::no_timeout, wait());
+            EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+            EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+            EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+            int index = radioRsp_v1_6->updatedRecordIndex;
+            EXPECT_TRUE(index > 0);
+
+            // Deleted a phonebook record
+            recordInfo.recordId = index;
+            recordInfo.name = "";
+            recordInfo.number = "";
+            serial = GetRandomSerialNumber();
+            radio_v1_6->updateSimPhonebookRecords(serial, recordInfo);
+
+            EXPECT_EQ(std::cv_status::no_timeout, wait());
+            EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo.type);
+            EXPECT_EQ(serial, radioRsp_v1_6->rspInfo.serial);
+            EXPECT_EQ(::android::hardware::radio::V1_6::RadioError::NONE, radioRsp_v1_6->rspInfo.error);
+        }
+    }
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
new file mode 100644
index 0000000..8c99d92
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_misc.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#include <regex>
+
+#include <android-base/logging.h>
+#include <radio_hidl_hal_utils_v1_6.h>
+
+/*
+ * Test IRadio.getAvailableNetworks() for the response returned.
+ */
+TEST_P(RadioHidlTest_v1_6, getAvailableNetworks) {
+    LOG(DEBUG) << "getAvailableNetworks";
+    serial = GetRandomSerialNumber();
+
+    radio_v1_6->getAvailableNetworks(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+    ASSERT_TRUE(radioRsp_v1_6->rspInfo_v1_0.type == RadioResponseType::SOLICITED ||
+                radioRsp_v1_6->rspInfo_v1_0.type == RadioResponseType::SOLICITED_ACK_EXP);
+
+    if (cardStatus.base.base.base.cardState == CardState::ABSENT) {
+        ASSERT_TRUE(CheckAnyOfErrors(
+                radioRsp_v1_6->rspInfo_v1_0.error,
+                {::android::hardware::radio::V1_0::RadioError::NONE,
+                 ::android::hardware::radio::V1_0::RadioError::CANCELLED,
+                 ::android::hardware::radio::V1_0::RadioError::DEVICE_IN_USE,
+                 ::android::hardware::radio::V1_0::RadioError::MODEM_ERR,
+                 ::android::hardware::radio::V1_0::RadioError::OPERATION_NOT_ALLOWED},
+                CHECK_GENERAL_ERROR));
+    } else if (radioRsp_v1_6->rspInfo_v1_0.error ==
+               ::android::hardware::radio::V1_0::RadioError::NONE) {
+        static const std::regex kOperatorNumericRe("^[0-9]{5,6}$");
+        for (OperatorInfo info : radioRsp_v1_6->networkInfos) {
+            ASSERT_TRUE(
+                    std::regex_match(std::string(info.operatorNumeric), kOperatorNumericRe));
+        }
+    }
+
+    LOG(DEBUG) << "getAvailableNetworks finished";
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_test.cpp b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
new file mode 100644
index 0000000..00f4468
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_test.cpp
@@ -0,0 +1,138 @@
+/*
+ * 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 <radio_hidl_hal_utils_v1_6.h>
+
+bool isServiceValidForDeviceConfiguration(hidl_string& serviceName) {
+    if (isSsSsEnabled()) {
+        // Device is configured as SSSS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME) {
+            ALOGI("%s instance is not valid for SSSS device.", serviceName.c_str());
+            return false;
+        }
+    } else if (isDsDsEnabled()) {
+        // Device is configured as DSDS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME && serviceName != RADIO_SERVICE_SLOT2_NAME) {
+            ALOGI("%s instance is not valid for DSDS device.", serviceName.c_str());
+            return false;
+        }
+    } else if (isTsTsEnabled()) {
+        // Device is configured as TSTS.
+        if (serviceName != RADIO_SERVICE_SLOT1_NAME && serviceName != RADIO_SERVICE_SLOT2_NAME &&
+            serviceName != RADIO_SERVICE_SLOT3_NAME) {
+            ALOGI("%s instance is not valid for TSTS device.", serviceName.c_str());
+            return false;
+        }
+    }
+    return true;
+}
+
+void RadioHidlTest_v1_6::SetUp() {
+    hidl_string serviceName = GetParam();
+
+    if (!isServiceValidForDeviceConfiguration(serviceName)) {
+        ALOGI("Skipped the test due to device configuration.");
+        GTEST_SKIP();
+    }
+
+    radio_v1_6 = android::hardware::radio::V1_6::IRadio::getService(serviceName);
+    ASSERT_NE(nullptr, radio_v1_6.get());
+
+    radioRsp_v1_6 = new (std::nothrow) RadioResponse_v1_6(*this);
+    ASSERT_NE(nullptr, radioRsp_v1_6.get());
+
+    count_ = 0;
+
+    radioInd_v1_6 = new (std::nothrow) RadioIndication_v1_6(*this);
+    ASSERT_NE(nullptr, radioInd_v1_6.get());
+
+    radio_v1_6->setResponseFunctions(radioRsp_v1_6, radioInd_v1_6);
+
+    updateSimCardStatus();
+    EXPECT_EQ(RadioResponseType::SOLICITED, radioRsp_v1_6->rspInfo_v1_0.type);
+    EXPECT_EQ(serial, radioRsp_v1_6->rspInfo_v1_0.serial);
+    EXPECT_EQ(::android::hardware::radio::V1_0::RadioError::NONE,
+              radioRsp_v1_6->rspInfo_v1_0.error);
+
+    sp<::android::hardware::radio::config::V1_1::IRadioConfig> radioConfig =
+            ::android::hardware::radio::config::V1_1::IRadioConfig::getService();
+    /* Enforce Vts testing with RadioConfig is existed. */
+    ASSERT_NE(nullptr, radioConfig.get());
+
+    /* Enforce Vts Testing with Sim Status Present only. */
+    EXPECT_EQ(CardState::PRESENT, cardStatus.base.base.base.cardState);
+}
+
+void RadioHidlTest_v1_6::clearPotentialEstablishedCalls() {
+    // Get the current call Id to hangup the established emergency call.
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getCurrentCalls_1_6(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+
+    // Hang up to disconnect the established call channels.
+    for (const ::android::hardware::radio::V1_6::Call& call : radioRsp_v1_6->currentCalls) {
+        serial = GetRandomSerialNumber();
+        radio_v1_6->hangup(serial, call.base.base.index);
+        ALOGI("Hang up to disconnect the established call channel: %d", call.base.base.index);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        // Give some time for modem to disconnect the established call channel.
+        sleep(MODEM_EMERGENCY_CALL_DISCONNECT_TIME);
+    }
+
+    // Verify there are no more current calls.
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getCurrentCalls_1_6(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+    EXPECT_EQ(0, radioRsp_v1_6->currentCalls.size());
+}
+
+void RadioHidlTest_v1_6::updateSimCardStatus() {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getIccCardStatus(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+}
+
+void RadioHidlTest_v1_6::getDataCallList() {
+    serial = GetRandomSerialNumber();
+    radio_v1_6->getDataCallList_1_6(serial);
+    EXPECT_EQ(std::cv_status::no_timeout, wait());
+}
+
+/**
+ * Specific features on the Radio Hal rely on Radio Hal Capabilities.  The VTS
+ * tests related to that features must not run if the related capability is
+ * disabled.
+ * <p/>
+ * Typical usage within VTS:
+ * if (getRadioHalCapabilities()) return;
+ */
+bool RadioHidlTest_v1_6::getRadioHalCapabilities() {
+    sp<::android::hardware::radio::config::V1_3::IRadioConfig> radioConfig_v1_3 =
+            ::android::hardware::radio::config::V1_3::IRadioConfig::getService();
+    if (radioConfig_v1_3.get() == nullptr) {
+        // If v1_3 isn't present, the values are initialized to false
+        return false;
+    } else {
+        // Get radioHalDeviceCapabilities from the radio config
+        sp<RadioConfigResponse> radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
+        radioConfig_v1_3->setResponseFunctions(radioConfigRsp, nullptr);
+        serial = GetRandomSerialNumber();
+
+        radioConfig_v1_3->getHalDeviceCapabilities(serial);
+        EXPECT_EQ(std::cv_status::no_timeout, wait());
+        return radioConfigRsp->modemReducedFeatureSet1;
+    }
+}
diff --git a/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
new file mode 100644
index 0000000..54c2977
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_hidl_hal_utils_v1_6.h
@@ -0,0 +1,1129 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#include "radio_config_hidl_hal_utils.h"
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/1.6/IRadio.h>
+#include <android/hardware/radio/1.6/IRadioIndication.h>
+#include <android/hardware/radio/1.6/IRadioResponse.h>
+#include <android/hardware/radio/1.6/types.h>
+
+#include "vts_test_util_v1_6.h"
+
+using namespace ::android::hardware::radio::V1_6;
+using namespace ::android::hardware::radio::V1_5;
+using namespace ::android::hardware::radio::V1_4;
+using namespace ::android::hardware::radio::V1_3;
+using namespace ::android::hardware::radio::V1_2;
+using namespace ::android::hardware::radio::V1_1;
+using namespace ::android::hardware::radio::V1_0;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+
+#define MODEM_EMERGENCY_CALL_ESTABLISH_TIME 3
+#define MODEM_EMERGENCY_CALL_DISCONNECT_TIME 3
+
+#define RADIO_SERVICE_SLOT1_NAME "slot1"  // HAL instance name for SIM slot 1 or single SIM device
+#define RADIO_SERVICE_SLOT2_NAME "slot2"  // HAL instance name for SIM slot 2 on dual SIM device
+#define RADIO_SERVICE_SLOT3_NAME "slot3"  // HAL instance name for SIM slot 3 on triple SIM device
+
+class RadioHidlTest_v1_6;
+extern ::android::hardware::radio::V1_5::CardStatus cardStatus;
+
+/* Callback class for radio response v1_6 */
+class RadioResponse_v1_6 : public ::android::hardware::radio::V1_6::IRadioResponse {
+  protected:
+    RadioResponseWaiter& parent_v1_6;
+
+  public:
+    hidl_vec<RadioBandMode> radioBandModes;
+    hidl_vec<OperatorInfo> networkInfos;
+
+    ::android::hardware::radio::V1_0::RadioResponseInfo rspInfo_v1_0;
+    ::android::hardware::radio::V1_6::RadioResponseInfo rspInfo;
+
+    // Call
+    hidl_vec<::android::hardware::radio::V1_6::Call> currentCalls;
+    ::android::hardware::radio::V1_2::VoiceRegStateResult voiceRegResp;
+
+    // Sms
+    SendSmsResult sendSmsResult;
+
+    // Modem
+    bool isModemEnabled;
+    bool enableModemResponseToggle;
+    bool isNRDualConnectivityEnabled;
+
+    // Pdu Session Id and Handover
+    int32_t allocatedPduSessionId;
+
+    ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_4::RadioAccessFamily>
+            networkTypeBitmapResponse;
+
+    // Data
+    ::android::hardware::radio::V1_4::DataRegStateResult dataRegResp;
+    ::android::hardware::radio::V1_6::SetupDataCallResult setupDataCallResult;
+
+    // SimLock status
+    ::android::hardware::radio::V1_4::CarrierRestrictionsWithPriority carrierRestrictionsResp;
+    ::android::hardware::radio::V1_4::SimLockMultiSimPolicy multiSimPolicyResp;
+
+    // Whether toggling uicc applications operation is supported.
+    bool canToggleUiccApplicationsEnablement;
+
+    // Whether Uicc applications are enabled or not.
+    bool areUiccApplicationsEnabled;
+
+    // Barring Info Response
+    ::android::hardware::radio::V1_5::CellIdentity barringCellIdentity;
+    ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo> barringInfos;
+
+    RadioResponse_v1_6(RadioResponseWaiter& parent_v1_6);
+
+    // Phone Book
+    ::android::hardware::radio::V1_6::PhonebookCapacity capacity;
+    int32_t updatedRecordIndex;
+
+    virtual ~RadioResponse_v1_6() = default;
+
+    Return<void> getIccCardStatusResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_0::CardStatus& cardStatus);
+
+    Return<void> supplyIccPinForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> supplyIccPukForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> supplyIccPin2ForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> supplyIccPuk2ForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> changeIccPinForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> changeIccPin2ForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> supplyNetworkDepersonalizationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            int32_t remainingRetries);
+
+    Return<void> getCurrentCallsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& calls);
+
+    Return<void> dialResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getIMSIForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& imsi);
+
+    Return<void> hangupConnectionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> hangupWaitingOrBackgroundResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> hangupForegroundResumeBackgroundResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> switchWaitingOrHoldingAndActiveResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> conferenceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> rejectCallResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getLastCallFailCauseResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const LastCallFailCauseInfo& failCauseInfo);
+
+    Return<void> getSignalStrengthResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_0::SignalStrength& sigStrength);
+
+    Return<void> getVoiceRegistrationStateResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_0::VoiceRegStateResult& voiceRegResponse);
+
+    Return<void> getDataRegistrationStateResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_0::DataRegStateResult& dataRegResponse);
+
+    Return<void> getOperatorResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& longName,
+            const ::android::hardware::hidl_string& shortName,
+            const ::android::hardware::hidl_string& numeric);
+
+    Return<void> setRadioPowerResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendDtmfResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendSmsResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                 const SendSmsResult& sms);
+
+    Return<void> sendSMSExpectMoreResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> setupDataCallResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const android::hardware::radio::V1_0::SetupDataCallResult& dcResponse);
+
+    Return<void> iccIOForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const IccIoResult& iccIo);
+
+    Return<void> sendUssdResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> cancelPendingUssdResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getClirResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                 int32_t n, int32_t m);
+
+    Return<void> setClirResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCallForwardStatusResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<CallForwardInfo>& call_forwardInfos);
+
+    Return<void> setCallForwardResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCallWaitingResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool enable,
+            int32_t serviceClass);
+
+    Return<void> setCallWaitingResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> acknowledgeLastIncomingGsmSmsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> acceptCallResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> deactivateDataCallResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getFacilityLockForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, int32_t response);
+
+    Return<void> setFacilityLockForAppResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, int32_t retry);
+
+    Return<void> setBarringPasswordResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getNetworkSelectionModeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool manual);
+
+    Return<void> setNetworkSelectionModeAutomaticResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setNetworkSelectionModeManualResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getAvailableNetworksResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<OperatorInfo>& networkInfos);
+
+    Return<void> startDtmfResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> stopDtmfResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getBasebandVersionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& version);
+
+    Return<void> separateConnectionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setMuteResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getMuteResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                 bool enable);
+
+    Return<void> getClipResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                 ClipStatus status);
+
+    Return<void> getDataCallListResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<
+                    android::hardware::radio::V1_0::SetupDataCallResult>& dcResponse);
+
+    Return<void> sendOemRilRequestRawResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<uint8_t>& data);
+
+    Return<void> sendOemRilRequestStringsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::hidl_string>& data);
+
+    Return<void> setSuppServiceNotificationsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> writeSmsToSimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, int32_t index);
+
+    Return<void> deleteSmsOnSimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setBandModeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getAvailableBandModesResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<RadioBandMode>& bandModes);
+
+    Return<void> sendEnvelopeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& commandResponse);
+
+    Return<void> sendTerminalResponseToSimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> handleStkCallSetupRequestFromSimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> explicitCallTransferResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setPreferredNetworkTypeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getPreferredNetworkTypeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            PreferredNetworkType nwType);
+
+    Return<void> getNeighboringCidsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<NeighboringCell>& cells);
+
+    Return<void> setLocationUpdatesResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setCdmaSubscriptionSourceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setCdmaRoamingPreferenceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCdmaRoamingPreferenceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, CdmaRoamingType type);
+
+    Return<void> setTTYModeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getTTYModeResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                    TtyMode mode);
+
+    Return<void> setPreferredVoicePrivacyResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getPreferredVoicePrivacyResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool enable);
+
+    Return<void> sendCDMAFeatureCodeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendBurstDtmfResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendCdmaSmsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> acknowledgeLastIncomingCdmaSmsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getGsmBroadcastConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<GsmBroadcastSmsConfigInfo>& configs);
+
+    Return<void> setGsmBroadcastConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setGsmBroadcastActivationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCdmaBroadcastConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<CdmaBroadcastSmsConfigInfo>& configs);
+
+    Return<void> setCdmaBroadcastConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setCdmaBroadcastActivationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCDMASubscriptionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& mdn,
+            const ::android::hardware::hidl_string& hSid,
+            const ::android::hardware::hidl_string& hNid,
+            const ::android::hardware::hidl_string& min,
+            const ::android::hardware::hidl_string& prl);
+
+    Return<void> writeSmsToRuimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, uint32_t index);
+
+    Return<void> deleteSmsOnRuimResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getDeviceIdentityResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& imei,
+            const ::android::hardware::hidl_string& imeisv,
+            const ::android::hardware::hidl_string& esn,
+            const ::android::hardware::hidl_string& meid);
+
+    Return<void> exitEmergencyCallbackModeResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getSmscAddressResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& smsc);
+
+    Return<void> setSmscAddressResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> reportSmsMemoryStatusResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> reportStkServiceIsRunningResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCdmaSubscriptionSourceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            CdmaSubscriptionSource source);
+
+    Return<void> requestIsimAuthenticationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_string& response);
+
+    Return<void> acknowledgeIncomingGsmSmsWithPduResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendEnvelopeWithStatusResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const IccIoResult& iccIo);
+
+    Return<void> getVoiceRadioTechnologyResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            ::android::hardware::radio::V1_0::RadioTechnology rat);
+
+    Return<void> getCellInfoListResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::CellInfo>&
+                    cellInfo);
+
+    Return<void> setCellInfoListRateResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setInitialAttachApnResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getImsRegistrationStateResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool isRegistered,
+            RadioTechnologyFamily ratFamily);
+
+    Return<void> sendImsSmsResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                    const SendSmsResult& sms);
+
+    Return<void> iccTransmitApduBasicChannelResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const IccIoResult& result);
+
+    Return<void> iccOpenLogicalChannelResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, int32_t channelId,
+            const ::android::hardware::hidl_vec<int8_t>& selectResponse);
+
+    Return<void> iccCloseLogicalChannelResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> iccTransmitApduLogicalChannelResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const IccIoResult& result);
+
+    Return<void> nvReadItemResponse(const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+                                    const ::android::hardware::hidl_string& result);
+
+    Return<void> nvWriteItemResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> nvWriteCdmaPrlResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> nvResetConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setUiccSubscriptionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setDataAllowedResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getHardwareConfigResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<HardwareConfig>& config);
+
+    Return<void> requestIccSimAuthenticationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const IccIoResult& result);
+
+    Return<void> setDataProfileResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> requestShutdownResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getRadioCapabilityResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const android::hardware::radio::V1_0::RadioCapability& rc);
+
+    Return<void> setRadioCapabilityResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const android::hardware::radio::V1_0::RadioCapability& rc);
+
+    Return<void> startLceServiceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const LceStatusInfo& statusInfo);
+
+    Return<void> stopLceServiceResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const LceStatusInfo& statusInfo);
+
+    Return<void> pullLceDataResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const LceDataInfo& lceInfo);
+
+    Return<void> getModemActivityInfoResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ActivityStatsInfo& activityInfo);
+
+    Return<void> setAllowedCarriersResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, int32_t numAllowed);
+
+    Return<void> getAllowedCarriersResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool allAllowed,
+            const CarrierRestrictions& carriers);
+
+    Return<void> sendDeviceStateResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setIndicationFilterResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setSimCardPowerResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> acknowledgeRequest(int32_t serial);
+
+    /* 1.1 Api */
+    Return<void> setCarrierInfoForImsiEncryptionResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setSimCardPowerResponse_1_1(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> startNetworkScanResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> stopNetworkScanResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> startKeepaliveResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const KeepaliveStatus& status);
+
+    Return<void> stopKeepaliveResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    /* 1.2 Api */
+    Return<void> setSignalStrengthReportingCriteriaResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setLinkCapacityReportingCriteriaResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getIccCardStatusResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_2::CardStatus& card_status);
+
+    Return<void> getCurrentCallsResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::Call>& calls);
+
+    Return<void> getSignalStrengthResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_2::SignalStrength& sig_strength);
+
+    Return<void> getSignalStrengthResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_4::SignalStrength& sig_strength);
+
+    Return<void> getCellInfoListResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::CellInfo>&
+                    cellInfo);
+
+    Return<void> getVoiceRegistrationStateResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_2::VoiceRegStateResult& voiceRegResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_2(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_2::DataRegStateResult& dataRegResponse);
+
+    /* 1.3 Api */
+    Return<void> setSystemSelectionChannelsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> enableModemResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getModemStackStatusResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, const bool enabled);
+
+    /* 1.4 Api */
+    Return<void> emergencyDialResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> startNetworkScanResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getCellInfoListResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_4::CellInfo>&
+                    cellInfo);
+
+    Return<void> getDataRegistrationStateResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_4::DataRegStateResult& dataRegResponse);
+
+    Return<void> getIccCardStatusResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_4::CardStatus& card_status);
+
+    Return<void> getPreferredNetworkTypeBitmapResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_bitfield<
+                    ::android::hardware::radio::V1_4::RadioAccessFamily>
+                    networkTypeBitmap);
+
+    Return<void> setPreferredNetworkTypeBitmapResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getDataCallListResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::V1_4::SetupDataCallResult>& dcResponse);
+
+    Return<void> setupDataCallResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const android::hardware::radio::V1_4::SetupDataCallResult& dcResponse);
+
+    Return<void> setAllowedCarriersResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getAllowedCarriersResponse_1_4(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const CarrierRestrictionsWithPriority& carriers, SimLockMultiSimPolicy multiSimPolicy);
+
+    /* 1.5 Api */
+    Return<void> setSignalStrengthReportingCriteriaResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setLinkCapacityReportingCriteriaResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> enableUiccApplicationsResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> areUiccApplicationsEnabledResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool enabled);
+
+    Return<void> canToggleUiccApplicationsEnablementResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info, bool canToggle);
+
+    Return<void> setSystemSelectionChannelsResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> startNetworkScanResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setupDataCallResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const android::hardware::radio::V1_5::SetupDataCallResult& dcResponse);
+
+    Return<void> getDataCallListResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const hidl_vec<::android::hardware::radio::V1_5::SetupDataCallResult>& dcResponse);
+
+    Return<void> setInitialAttachApnResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setDataProfileResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setRadioPowerResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> setIndicationFilterResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> getBarringInfoResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::CellIdentity& cellIdentity,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
+                    barringInfos);
+
+    Return<void> getVoiceRegistrationStateResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::RegStateResult& regResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::RegStateResult& regResponse);
+
+    Return<void> getCellInfoListResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::CellInfo>&
+                    cellInfo);
+
+    Return<void> setNetworkSelectionModeManualResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info);
+
+    Return<void> sendCdmaSmsExpectMoreResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> supplySimDepersonalizationResponse(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            ::android::hardware::radio::V1_5::PersoSubstate persoType, int32_t remainingRetries);
+
+    Return<void> getIccCardStatusResponse_1_5(
+            const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_5::CardStatus& card_status);
+
+    /* 1.6 Api */
+    Return<void> setRadioPowerResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> setupDataCallResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const android::hardware::radio::V1_6::SetupDataCallResult& dcResponse);
+
+    Return<void> getDataCallListResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const hidl_vec<::android::hardware::radio::V1_6::SetupDataCallResult>& dcResponse);
+
+    Return<void> sendSmsResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> sendSmsExpectMoreResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> sendCdmaSmsResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> setSimCardPowerResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> sendCdmaSmsExpectMoreResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const SendSmsResult& sms);
+
+    Return<void> setNrDualConnectivityStateResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+    Return<void> isNrDualConnectivityEnabledResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info, bool isEnabled);
+
+    Return<void> allocatePduSessionIdResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info, int32_t id);
+
+    Return<void> releasePduSessionIdResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> startHandoverResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> cancelHandoverResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> setAllowedNetworkTypesBitmapResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getAllowedNetworkTypesBitmapResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_bitfield<
+                    ::android::hardware::radio::V1_4::RadioAccessFamily>
+                    networkTypeBitmap);
+
+    Return<void> setDataThrottlingResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getSystemSelectionChannelsResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const hidl_vec<::android::hardware::radio::V1_5::RadioAccessSpecifier>& specifier);
+
+    Return<void> getSignalStrengthResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::SignalStrength& sig_strength);
+
+    Return<void> getCellInfoListResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    cellInfo);
+
+    Return<void> getVoiceRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
+
+    Return<void> getDataRegistrationStateResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::RegStateResult& regResponse);
+
+    Return<void> getCurrentCallsResponse_1_6(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& calls);
+
+    Return<void> getSlicingConfigResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::SlicingConfig& slicingConfig);
+
+    Return<void> getSimPhonebookRecordsResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info);
+
+    Return<void> getSimPhonebookCapacityResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            const ::android::hardware::radio::V1_6::PhonebookCapacity& capacity);
+
+    Return<void> updateSimPhonebookRecordsResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            int32_t updatedRecordIndex);
+};
+
+/* Callback class for radio indication */
+class RadioIndication_v1_6 : public ::android::hardware::radio::V1_6::IRadioIndication {
+  protected:
+    RadioHidlTest_v1_6& parent_v1_6;
+
+  public:
+    RadioIndication_v1_6(RadioHidlTest_v1_6& parent_v1_6);
+    virtual ~RadioIndication_v1_6() = default;
+
+    /* 1.6 Api */
+    Return<void> dataCallListChanged_1_6(
+            RadioIndicationType type,
+            const hidl_vec<::android::hardware::radio::V1_6::SetupDataCallResult>& dcList);
+
+    Return<void> unthrottleApn(RadioIndicationType type,
+                               const ::android::hardware::hidl_string& apn);
+
+    Return<void> currentSignalStrength_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::SignalStrength& signalStrength);
+
+    Return<void> networkScanResult_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::CellInfo>&
+                    records);
+
+    Return<void> currentPhysicalChannelConfigs_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::V1_6::PhysicalChannelConfig>& configs);
+
+    /* 1.5 Api */
+    Return<void> uiccApplicationsEnablementChanged(RadioIndicationType type, bool enabled);
+
+    Return<void> networkScanResult_1_5(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_5::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_5(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::CellInfo>&
+                    records);
+
+    Return<void> dataCallListChanged_1_5(
+            RadioIndicationType type,
+            const hidl_vec<::android::hardware::radio::V1_5::SetupDataCallResult>& dcList);
+
+    /* 1.4 Api */
+    Return<void> currentEmergencyNumberList(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<EmergencyNumber>& emergencyNumberList);
+
+    Return<void> cellInfoList_1_4(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_4::CellInfo>&
+                    records);
+
+    Return<void> networkScanResult_1_4(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_4::NetworkScanResult& result);
+
+    Return<void> currentPhysicalChannelConfigs_1_4(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::V1_4::PhysicalChannelConfig>& configs);
+
+    Return<void> dataCallListChanged_1_4(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<
+                    android::hardware::radio::V1_4::SetupDataCallResult>& dcList);
+
+    /* 1.2 Api */
+    Return<void> networkScanResult_1_2(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_2::NetworkScanResult& result);
+
+    Return<void> cellInfoList_1_2(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::CellInfo>&
+                    records);
+
+    Return<void> currentLinkCapacityEstimate(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_2::LinkCapacityEstimate& lce);
+
+    Return<void> currentLinkCapacityEstimate_1_6(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_6::LinkCapacityEstimate& lce);
+
+    Return<void> currentPhysicalChannelConfigs(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::V1_2::PhysicalChannelConfig>& configs);
+
+    Return<void> currentSignalStrength_1_2(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_2::SignalStrength& signalStrength);
+
+    Return<void> currentSignalStrength_1_4(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_4::SignalStrength& signalStrength);
+
+    /* 1.1 Api */
+    Return<void> carrierInfoForImsiEncryption(RadioIndicationType info);
+
+    Return<void> networkScanResult(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_1::NetworkScanResult& result);
+
+    Return<void> keepaliveStatus(RadioIndicationType type, const KeepaliveStatus& status);
+
+    /* 1.0 Api */
+    Return<void> radioStateChanged(RadioIndicationType type, RadioState radioState);
+
+    Return<void> callStateChanged(RadioIndicationType type);
+
+    Return<void> networkStateChanged(RadioIndicationType type);
+
+    Return<void> newSms(RadioIndicationType type,
+                        const ::android::hardware::hidl_vec<uint8_t>& pdu);
+
+    Return<void> newSmsStatusReport(RadioIndicationType type,
+                                    const ::android::hardware::hidl_vec<uint8_t>& pdu);
+
+    Return<void> newSmsOnSim(RadioIndicationType type, int32_t recordNumber);
+
+    Return<void> onUssd(RadioIndicationType type, UssdModeType modeType,
+                        const ::android::hardware::hidl_string& msg);
+
+    Return<void> nitzTimeReceived(RadioIndicationType type,
+                                  const ::android::hardware::hidl_string& nitzTime,
+                                  uint64_t receivedTime);
+
+    Return<void> currentSignalStrength(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_0::SignalStrength& signalStrength);
+
+    Return<void> dataCallListChanged(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<
+                    android::hardware::radio::V1_0::SetupDataCallResult>& dcList);
+
+    Return<void> suppSvcNotify(RadioIndicationType type, const SuppSvcNotification& suppSvc);
+
+    Return<void> stkSessionEnd(RadioIndicationType type);
+
+    Return<void> stkProactiveCommand(RadioIndicationType type,
+                                     const ::android::hardware::hidl_string& cmd);
+
+    Return<void> stkEventNotify(RadioIndicationType type,
+                                const ::android::hardware::hidl_string& cmd);
+
+    Return<void> stkCallSetup(RadioIndicationType type, int64_t timeout);
+
+    Return<void> simSmsStorageFull(RadioIndicationType type);
+
+    Return<void> simRefresh(RadioIndicationType type, const SimRefreshResult& refreshResult);
+
+    Return<void> callRing(RadioIndicationType type, bool isGsm, const CdmaSignalInfoRecord& record);
+
+    Return<void> simStatusChanged(RadioIndicationType type);
+
+    Return<void> cdmaNewSms(RadioIndicationType type, const CdmaSmsMessage& msg);
+
+    Return<void> newBroadcastSms(RadioIndicationType type,
+                                 const ::android::hardware::hidl_vec<uint8_t>& data);
+
+    Return<void> cdmaRuimSmsStorageFull(RadioIndicationType type);
+
+    Return<void> restrictedStateChanged(RadioIndicationType type, PhoneRestrictedState state);
+
+    Return<void> enterEmergencyCallbackMode(RadioIndicationType type);
+
+    Return<void> cdmaCallWaiting(RadioIndicationType type,
+                                 const CdmaCallWaiting& callWaitingRecord);
+
+    Return<void> cdmaOtaProvisionStatus(RadioIndicationType type, CdmaOtaProvisionStatus status);
+
+    Return<void> cdmaInfoRec(RadioIndicationType type, const CdmaInformationRecords& records);
+
+    Return<void> indicateRingbackTone(RadioIndicationType type, bool start);
+
+    Return<void> resendIncallMute(RadioIndicationType type);
+
+    Return<void> cdmaSubscriptionSourceChanged(RadioIndicationType type,
+                                               CdmaSubscriptionSource cdmaSource);
+
+    Return<void> cdmaPrlChanged(RadioIndicationType type, int32_t version);
+
+    Return<void> exitEmergencyCallbackMode(RadioIndicationType type);
+
+    Return<void> rilConnected(RadioIndicationType type);
+
+    Return<void> voiceRadioTechChanged(RadioIndicationType type,
+                                       ::android::hardware::radio::V1_0::RadioTechnology rat);
+
+    Return<void> cellInfoList(
+            RadioIndicationType type,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::CellInfo>&
+                    records);
+
+    Return<void> imsNetworkStateChanged(RadioIndicationType type);
+
+    Return<void> subscriptionStatusChanged(RadioIndicationType type, bool activate);
+
+    Return<void> srvccStateNotify(RadioIndicationType type, SrvccState state);
+
+    Return<void> hardwareConfigChanged(
+            RadioIndicationType type, const ::android::hardware::hidl_vec<HardwareConfig>& configs);
+
+    Return<void> radioCapabilityIndication(
+            RadioIndicationType type, const android::hardware::radio::V1_0::RadioCapability& rc);
+
+    Return<void> onSupplementaryServiceIndication(RadioIndicationType type,
+                                                  const StkCcUnsolSsResult& ss);
+
+    Return<void> stkCallControlAlphaNotify(RadioIndicationType type,
+                                           const ::android::hardware::hidl_string& alpha);
+
+    Return<void> lceData(RadioIndicationType type, const LceDataInfo& lce);
+
+    Return<void> pcoData(RadioIndicationType type, const PcoDataInfo& pco);
+
+    Return<void> modemReset(RadioIndicationType type,
+                            const ::android::hardware::hidl_string& reason);
+
+    Return<void> registrationFailed(
+            RadioIndicationType type,
+            const ::android::hardware::radio::V1_5::CellIdentity& cellIdentity,
+            const ::android::hardware::hidl_string& chosenPlmn,
+            ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_5::Domain> domain,
+            int32_t causeCode, int32_t additionalCauseCode);
+
+    Return<void> barringInfoChanged(
+            RadioIndicationType /*type*/,
+            const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
+            /*barringInfos*/);
+
+    Return<void> simPhonebookChanged(RadioIndicationType type);
+
+    Return<void> simPhonebookRecordsReceived(
+            RadioIndicationType type,
+            ::android::hardware::radio::V1_6::PbReceivedStatus status,
+            const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::PhonebookRecordInfo>&
+                    records);
+};
+
+// The main test class for Radio HIDL.
+class RadioHidlTest_v1_6 : public ::testing::TestWithParam<std::string>,
+                           public RadioResponseWaiter {
+  protected:
+    /* Clear Potential Established Calls */
+    void clearPotentialEstablishedCalls();
+
+    /* Update Sim Card Status */
+    void updateSimCardStatus();
+
+    /* Get current data call list */
+    void getDataCallList();
+
+  public:
+    virtual void SetUp() override;
+
+    bool getRadioHalCapabilities();
+
+    /* radio service handle */
+    sp<::android::hardware::radio::V1_6::IRadio> radio_v1_6;
+
+    /* radio response handle */
+    sp<RadioResponse_v1_6> radioRsp_v1_6;
+
+    /* radio indication handle */
+    sp<RadioIndication_v1_6> radioInd_v1_6;
+};
diff --git a/radio/1.6/vts/functional/radio_indication.cpp b/radio/1.6/vts/functional/radio_indication.cpp
new file mode 100644
index 0000000..8292131
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_indication.cpp
@@ -0,0 +1,427 @@
+/*
+ * 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 <radio_hidl_hal_utils_v1_6.h>
+
+RadioIndication_v1_6::RadioIndication_v1_6(RadioHidlTest_v1_6& parent) : parent_v1_6(parent) {}
+
+/* 1.6 Apis */
+Return<void> RadioIndication_v1_6::dataCallListChanged_1_6(
+        RadioIndicationType /*type*/,
+        const hidl_vec<android::hardware::radio::V1_6::SetupDataCallResult>& /*dcList*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::unthrottleApn(RadioIndicationType /*type*/,
+                                                 const ::android::hardware::hidl_string& /*apn*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentPhysicalChannelConfigs_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::PhysicalChannelConfig>& /*configs*/) {
+    return Void();
+}
+
+/* 1.5 Apis */
+Return<void> RadioIndication_v1_6::uiccApplicationsEnablementChanged(RadioIndicationType /*type*/,
+                                                                     bool /*enabled*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::registrationFailed(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
+        const hidl_string& /*chosenPlmn*/,
+        ::android::hardware::hidl_bitfield<::android::hardware::radio::V1_5::Domain> /*domain*/,
+        int32_t /*causeCode*/, int32_t /*additionalCauseCode*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::barringInfoChanged(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
+        const hidl_vec<::android::hardware::radio::V1_5::BarringInfo>& /*barringInfos*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult_1_5(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_5::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_5(
+        RadioIndicationType /*type*/,
+        const hidl_vec<::android::hardware::radio::V1_5::CellInfo>& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::dataCallListChanged_1_5(
+        RadioIndicationType /*type*/,
+        const hidl_vec<android::hardware::radio::V1_5::SetupDataCallResult>& /*dcList*/) {
+    return Void();
+}
+
+/* 1.4 Apis */
+Return<void> RadioIndication_v1_6::currentPhysicalChannelConfigs_1_4(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_4::PhysicalChannelConfig>& /*configs*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult_1_4(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_4::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_4(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_4::CellInfo>& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentEmergencyNumberList(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<EmergencyNumber>& /*emergencyNumberList*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::dataCallListChanged_1_4(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<android::hardware::radio::V1_4::SetupDataCallResult>&
+        /*dcList*/) {
+    return Void();
+}
+
+/* 1.2 Apis */
+Return<void> RadioIndication_v1_6::networkScanResult_1_2(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_2::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_2(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_2::CellInfo>& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentLinkCapacityEstimate(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_2::LinkCapacityEstimate& /*lce*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentLinkCapacityEstimate_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::LinkCapacityEstimate& /*lce*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentPhysicalChannelConfigs(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_2::PhysicalChannelConfig>& /*configs*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentSignalStrength_1_2(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_2::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentSignalStrength_1_4(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+/* 1.1 Apis */
+Return<void> RadioIndication_v1_6::carrierInfoForImsiEncryption(RadioIndicationType /*info*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_1::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::keepaliveStatus(RadioIndicationType /*type*/,
+                                                   const KeepaliveStatus& /*status*/) {
+    return Void();
+}
+
+/* 1.0 Apis */
+Return<void> RadioIndication_v1_6::radioStateChanged(RadioIndicationType /*type*/,
+                                                     RadioState /*radioState*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::callStateChanged(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkStateChanged(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::newSms(RadioIndicationType /*type*/,
+                                          const ::android::hardware::hidl_vec<uint8_t>& /*pdu*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::newSmsStatusReport(
+        RadioIndicationType /*type*/, const ::android::hardware::hidl_vec<uint8_t>& /*pdu*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::newSmsOnSim(RadioIndicationType /*type*/,
+                                               int32_t /*recordNumber*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::onUssd(RadioIndicationType /*type*/, UssdModeType /*modeType*/,
+                                          const ::android::hardware::hidl_string& /*msg*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::nitzTimeReceived(
+        RadioIndicationType /*type*/, const ::android::hardware::hidl_string& /*nitzTime*/,
+        uint64_t /*receivedTime*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentSignalStrength(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_0::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::dataCallListChanged(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<android::hardware::radio::V1_0::SetupDataCallResult>&
+        /*dcList*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::suppSvcNotify(RadioIndicationType /*type*/,
+                                                 const SuppSvcNotification& /*suppSvc*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::stkSessionEnd(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::stkProactiveCommand(
+        RadioIndicationType /*type*/, const ::android::hardware::hidl_string& /*cmd*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::stkEventNotify(RadioIndicationType /*type*/,
+                                                  const ::android::hardware::hidl_string& /*cmd*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::stkCallSetup(RadioIndicationType /*type*/, int64_t /*timeout*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::simSmsStorageFull(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::simRefresh(RadioIndicationType /*type*/,
+                                              const SimRefreshResult& /*refreshResult*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::callRing(RadioIndicationType /*type*/, bool /*isGsm*/,
+                                            const CdmaSignalInfoRecord& /*record*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::simStatusChanged(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaNewSms(RadioIndicationType /*type*/,
+                                              const CdmaSmsMessage& /*msg*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::newBroadcastSms(
+        RadioIndicationType /*type*/, const ::android::hardware::hidl_vec<uint8_t>& /*data*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaRuimSmsStorageFull(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::restrictedStateChanged(RadioIndicationType /*type*/,
+                                                          PhoneRestrictedState /*state*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::enterEmergencyCallbackMode(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaCallWaiting(RadioIndicationType /*type*/,
+                                                   const CdmaCallWaiting& /*callWaitingRecord*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaOtaProvisionStatus(RadioIndicationType /*type*/,
+                                                          CdmaOtaProvisionStatus /*status*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaInfoRec(RadioIndicationType /*type*/,
+                                               const CdmaInformationRecords& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::indicateRingbackTone(RadioIndicationType /*type*/,
+                                                        bool /*start*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::resendIncallMute(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaSubscriptionSourceChanged(
+        RadioIndicationType /*type*/, CdmaSubscriptionSource /*cdmaSource*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cdmaPrlChanged(RadioIndicationType /*type*/,
+                                                  int32_t /*version*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::exitEmergencyCallbackMode(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::rilConnected(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::voiceRadioTechChanged(
+        RadioIndicationType /*type*/, ::android::hardware::radio::V1_0::RadioTechnology /*rat*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_0::CellInfo>& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::imsNetworkStateChanged(RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::subscriptionStatusChanged(RadioIndicationType /*type*/,
+                                                             bool /*activate*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::srvccStateNotify(RadioIndicationType /*type*/,
+                                                    SrvccState /*state*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::hardwareConfigChanged(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<HardwareConfig>& /*configs*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::radioCapabilityIndication(
+        RadioIndicationType /*type*/,
+        const android::hardware::radio::V1_0::RadioCapability& /*rc*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::onSupplementaryServiceIndication(
+        RadioIndicationType /*type*/, const StkCcUnsolSsResult& /*ss*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::stkCallControlAlphaNotify(
+        RadioIndicationType /*type*/, const ::android::hardware::hidl_string& /*alpha*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::lceData(RadioIndicationType /*type*/,
+                                           const LceDataInfo& /*lce*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::pcoData(RadioIndicationType /*type*/,
+                                           const PcoDataInfo& /*pco*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::modemReset(RadioIndicationType /*type*/,
+                                              const ::android::hardware::hidl_string& /*reason*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::currentSignalStrength_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*signalStrength*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::networkScanResult_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::radio::V1_6::NetworkScanResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::cellInfoList_1_6(
+        RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*records*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::simPhonebookChanged(
+        RadioIndicationType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioIndication_v1_6::simPhonebookRecordsReceived(
+        RadioIndicationType /*type*/,
+        ::android::hardware::radio::V1_6::PbReceivedStatus /*status*/,
+        const ::android::hardware::hidl_vec<
+        ::android::hardware::radio::V1_6::PhonebookRecordInfo>& /*records*/) {
+    return Void();
+}
diff --git a/radio/1.6/vts/functional/radio_response.cpp b/radio/1.6/vts/functional/radio_response.cpp
new file mode 100644
index 0000000..e1b9f56
--- /dev/null
+++ b/radio/1.6/vts/functional/radio_response.cpp
@@ -0,0 +1,1270 @@
+/*
+ * 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 <radio_hidl_hal_utils_v1_6.h>
+
+::android::hardware::radio::V1_5::CardStatus cardStatus;
+
+RadioResponse_v1_6::RadioResponse_v1_6(RadioResponseWaiter& parent) : parent_v1_6(parent) {}
+
+/* 1.0 Apis */
+Return<void> RadioResponse_v1_6::getIccCardStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_0::CardStatus& /*card_status*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplyIccPinForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplyIccPukForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplyIccPin2ForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplyIccPuk2ForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::changeIccPinForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::changeIccPin2ForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplyNetworkDepersonalizationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCurrentCallsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<::android::hardware::radio::V1_0::Call>& /*calls*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::dialResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getIMSIForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*imsi*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::hangupConnectionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::hangupWaitingOrBackgroundResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::hangupForegroundResumeBackgroundResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::switchWaitingOrHoldingAndActiveResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::conferenceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::rejectCallResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getLastCallFailCauseResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const LastCallFailCauseInfo& /*failCauseInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_0::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_0::VoiceRegStateResult& /*voiceRegResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_0::DataRegStateResult& /*dataRegResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getOperatorResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*longName*/,
+        const ::android::hardware::hidl_string& /*shortName*/,
+        const ::android::hardware::hidl_string& /*numeric*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setRadioPowerResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendDtmfResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendSmsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const SendSmsResult& /*sms*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendSMSExpectMoreResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const SendSmsResult& /*sms*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setupDataCallResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const android::hardware::radio::V1_0::SetupDataCallResult& /*dcResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::iccIOForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const IccIoResult& /*iccIo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendUssdResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::cancelPendingUssdResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getClirResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, int32_t /*n*/,
+        int32_t /*m*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setClirResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCallForwardStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<CallForwardInfo>&
+        /*callForwardInfos*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCallForwardResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCallWaitingResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*enable*/,
+        int32_t /*serviceClass*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCallWaitingResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::acknowledgeLastIncomingGsmSmsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::acceptCallResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::deactivateDataCallResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getFacilityLockForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, int32_t /*response*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setFacilityLockForAppResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, int32_t /*retry*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setBarringPasswordResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getNetworkSelectionModeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*manual*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setNetworkSelectionModeAutomaticResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setNetworkSelectionModeManualResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getAvailableNetworksResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const ::android::hardware::hidl_vec<OperatorInfo>& networkInfos) {
+    rspInfo_v1_0 = info;
+    this->networkInfos = networkInfos;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startDtmfResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::stopDtmfResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getBasebandVersionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*version*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::separateConnectionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setMuteResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getMuteResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*enable*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getClipResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        ClipStatus /*status*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataCallListResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<android::hardware::radio::V1_0::SetupDataCallResult>&
+        /*dcResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendOemRilRequestRawResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<uint8_t>& /*data*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendOemRilRequestStringsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<::android::hardware::hidl_string>& /*data*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSuppServiceNotificationsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::writeSmsToSimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, int32_t /*index*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::deleteSmsOnSimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setBandModeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getAvailableBandModesResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<RadioBandMode>& /*bandModes*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendEnvelopeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*commandResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendTerminalResponseToSimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::handleStkCallSetupRequestFromSimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::explicitCallTransferResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setPreferredNetworkTypeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getPreferredNetworkTypeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        PreferredNetworkType /*nw_type*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getNeighboringCidsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<NeighboringCell>& /*cells*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setLocationUpdatesResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCdmaSubscriptionSourceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCdmaRoamingPreferenceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCdmaRoamingPreferenceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        CdmaRoamingType /*type*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setTTYModeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getTTYModeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, TtyMode /*mode*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setPreferredVoicePrivacyResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getPreferredVoicePrivacyResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*enable*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendCDMAFeatureCodeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendBurstDtmfResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendCdmaSmsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const SendSmsResult& /*sms*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::acknowledgeLastIncomingCdmaSmsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getGsmBroadcastConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<GsmBroadcastSmsConfigInfo>& /*configs*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setGsmBroadcastConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setGsmBroadcastActivationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCdmaBroadcastConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<CdmaBroadcastSmsConfigInfo>& /*configs*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCdmaBroadcastConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCdmaBroadcastActivationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCDMASubscriptionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*mdn*/,
+        const ::android::hardware::hidl_string& /*hSid*/,
+        const ::android::hardware::hidl_string& /*hNid*/,
+        const ::android::hardware::hidl_string& /*min*/,
+        const ::android::hardware::hidl_string& /*prl*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::writeSmsToRuimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, uint32_t /*index*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::deleteSmsOnRuimResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDeviceIdentityResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*imei*/,
+        const ::android::hardware::hidl_string& /*imeisv*/,
+        const ::android::hardware::hidl_string& /*esn*/,
+        const ::android::hardware::hidl_string& /*meid*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::exitEmergencyCallbackModeResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSmscAddressResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*smsc*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSmscAddressResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::reportSmsMemoryStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::reportStkServiceIsRunningResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCdmaSubscriptionSourceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        CdmaSubscriptionSource /*source*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::requestIsimAuthenticationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*response*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::acknowledgeIncomingGsmSmsWithPduResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendEnvelopeWithStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const IccIoResult& /*iccIo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRadioTechnologyResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        ::android::hardware::radio::V1_0::RadioTechnology /*rat*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_0::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setCellInfoListRateResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setInitialAttachApnResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getImsRegistrationStateResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*isRegistered*/,
+        RadioTechnologyFamily /*ratFamily*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendImsSmsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const SendSmsResult& /*sms*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::iccTransmitApduBasicChannelResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const IccIoResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::iccOpenLogicalChannelResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, int32_t /*channelId*/,
+        const ::android::hardware::hidl_vec<int8_t>& /*selectResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::iccCloseLogicalChannelResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::iccTransmitApduLogicalChannelResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const IccIoResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::nvReadItemResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_string& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::nvWriteItemResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::nvWriteCdmaPrlResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::nvResetConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setUiccSubscriptionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setDataAllowedResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getHardwareConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<HardwareConfig>& /*config*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::requestIccSimAuthenticationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const IccIoResult& /*result*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setDataProfileResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::requestShutdownResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getRadioCapabilityResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const android::hardware::radio::V1_0::RadioCapability& /*rc*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setRadioCapabilityResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const android::hardware::radio::V1_0::RadioCapability& /*rc*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startLceServiceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const LceStatusInfo& /*statusInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::stopLceServiceResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const LceStatusInfo& /*statusInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::pullLceDataResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const LceDataInfo& /*lceInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getModemActivityInfoResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ActivityStatsInfo& /*activityInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setAllowedCarriersResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        int32_t /*numAllowed*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getAllowedCarriersResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*allAllowed*/,
+        const CarrierRestrictions& /*carriers*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendDeviceStateResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setIndicationFilterResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSimCardPowerResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::acknowledgeRequest(int32_t /*serial*/) {
+    return Void();
+}
+
+/* 1.1 Apis */
+Return<void> RadioResponse_v1_6::setCarrierInfoForImsiEncryptionResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSimCardPowerResponse_1_1(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startNetworkScanResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::stopNetworkScanResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startKeepaliveResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const KeepaliveStatus& /*status*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::stopKeepaliveResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+/* 1.2 Apis */
+Return<void> RadioResponse_v1_6::setSignalStrengthReportingCriteriaResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setLinkCapacityReportingCriteriaResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getIccCardStatusResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_2::CardStatus& /*card_status*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCurrentCallsResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<::android::hardware::radio::V1_2::Call>& /*calls*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_2::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_2::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_2::VoiceRegStateResult& voiceRegResponse) {
+    rspInfo_v1_0 = info;
+    voiceRegResp = voiceRegResponse;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_2::DataRegStateResult& /*dataRegResponse*/) {
+    return Void();
+}
+
+/* 1.3 Apis */
+Return<void> RadioResponse_v1_6::setSystemSelectionChannelsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::enableModemResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getModemStackStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const bool /*enabled*/) {
+    return Void();
+}
+
+/* 1.4 Apis */
+Return<void> RadioResponse_v1_6::emergencyDialResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info) {
+    rspInfo_v1_0 = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startNetworkScanResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_4::DataRegStateResult& /*dataRegResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_4::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_4::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getIccCardStatusResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_4::CardStatus& /*card_status*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getPreferredNetworkTypeBitmapResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_bitfield<
+                ::android::hardware::radio::V1_4::RadioAccessFamily>
+        /*networkTypeBitmap*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setPreferredNetworkTypeBitmapResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataCallListResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<::android::hardware::radio::V1_4::SetupDataCallResult>&
+        /*dcResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setupDataCallResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const android::hardware::radio::V1_4::SetupDataCallResult& /*dcResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setAllowedCarriersResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getAllowedCarriersResponse_1_4(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const CarrierRestrictionsWithPriority& /*carriers*/,
+        SimLockMultiSimPolicy /*multiSimPolicy*/) {
+    return Void();
+}
+
+/* 1.5 Apis */
+Return<void> RadioResponse_v1_6::setSignalStrengthReportingCriteriaResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setLinkCapacityReportingCriteriaResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::enableUiccApplicationsResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::areUiccApplicationsEnabledResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*enabled*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::canToggleUiccApplicationsEnablementResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/, bool /*canToggle*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSystemSelectionChannelsResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startNetworkScanResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setupDataCallResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const android::hardware::radio::V1_5::SetupDataCallResult& /* dcResponse */) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataCallListResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const hidl_vec<::android::hardware::radio::V1_5::SetupDataCallResult>& /* dcResponse */) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setInitialAttachApnResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setDataProfileResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setRadioPowerResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setIndicationFilterResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getBarringInfoResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_5::CellIdentity& /*cellIdentity*/,
+        const ::android::hardware::hidl_vec<::android::hardware::radio::V1_5::BarringInfo>&
+        /*barringInfos*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_5::RegStateResult& /*regResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_5::RegStateResult& /*regResponse*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_5::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setNetworkSelectionModeManualResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendCdmaSmsExpectMoreResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        const SendSmsResult& /*sms*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::supplySimDepersonalizationResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /*info*/,
+        ::android::hardware::radio::V1_5::PersoSubstate /*persoType*/,
+        int32_t /*remainingRetries*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getIccCardStatusResponse_1_5(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_5::CardStatus& card_status) {
+    rspInfo_v1_0 = info;
+    cardStatus = card_status;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+/* 1.6 Apis */
+Return<void> RadioResponse_v1_6::setRadioPowerResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setupDataCallResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const android::hardware::radio::V1_6::SetupDataCallResult& dcResponse) {
+    rspInfo = info;
+    setupDataCallResult = dcResponse;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setNrDualConnectivityStateResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataCallListResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const hidl_vec<::android::hardware::radio::V1_6::SetupDataCallResult>& /* dcResponse */) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendSmsResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const SendSmsResult& sms) {
+    rspInfo = info;
+    sendSmsResult = sms;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendSmsExpectMoreResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const SendSmsResult& sms) {
+    rspInfo = info;
+    sendSmsResult = sms;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendCdmaSmsResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const SendSmsResult& sms) {
+    rspInfo = info;
+    sendSmsResult = sms;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setSimCardPowerResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::sendCdmaSmsExpectMoreResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const SendSmsResult& sms) {
+    rspInfo = info;
+    sendSmsResult = sms;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::isNrDualConnectivityEnabledResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info, bool isEnabled) {
+    rspInfo = info;
+    isNRDualConnectivityEnabled = isEnabled;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::allocatePduSessionIdResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info, int32_t id) {
+    rspInfo = info;
+    allocatedPduSessionId = id;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::releasePduSessionIdResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::startHandoverResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::cancelHandoverResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setAllowedNetworkTypesBitmapResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getAllowedNetworkTypesBitmapResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::hidl_bitfield<
+                ::android::hardware::radio::V1_4::RadioAccessFamily>
+                networkTypeBitmap) {
+    rspInfo = info;
+    networkTypeBitmapResponse = networkTypeBitmap;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::setDataThrottlingResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSignalStrengthResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::radio::V1_6::SignalStrength& /*sig_strength*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCellInfoListResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /*info*/,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::V1_6::CellInfo>& /*cellInfo*/) {
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSystemSelectionChannelsResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const hidl_vec<::android::hardware::radio::V1_5::RadioAccessSpecifier>& /*specifier*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getVoiceRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& regResponse) {
+    rspInfo = info;
+    voiceRegResp.regState = regResponse.regState;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getDataRegistrationStateResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::RegStateResult& /*regResponse*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getCurrentCallsResponse_1_6(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::hidl_vec<::android::hardware::radio::V1_6::Call>& calls) {
+    rspInfo = info;
+    currentCalls = calls;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSlicingConfigResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::SlicingConfig& /*slicingConfig*/) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSimPhonebookRecordsResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info) {
+    rspInfo = info;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::getSimPhonebookCapacityResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        const ::android::hardware::radio::V1_6::PhonebookCapacity& capacity) {
+    rspInfo = info;
+    this->capacity = capacity;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioResponse_v1_6::updateSimPhonebookRecordsResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+        int32_t updatedRecordIndex) {
+    rspInfo = info;
+    this->updatedRecordIndex = updatedRecordIndex;
+    parent_v1_6.notify(info.serial);
+    return Void();
+}
diff --git a/radio/1.6/vts/functional/vts_test_util_v1_6.cpp b/radio/1.6/vts/functional/vts_test_util_v1_6.cpp
new file mode 100644
index 0000000..2653865
--- /dev/null
+++ b/radio/1.6/vts/functional/vts_test_util_v1_6.cpp
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "RadioTest"
+
+#include <vts_test_util_v1_6.h>
+#include <iostream>
+#include "VtsCoreUtil.h"
+
+::testing::AssertionResult CheckAnyOfErrors(
+        ::android::hardware::radio::V1_6::RadioError err,
+        std::vector<::android::hardware::radio::V1_6::RadioError> errors, CheckFlag flag) {
+    const static vector<::android::hardware::radio::V1_6::RadioError> generalErrors = {
+            ::android::hardware::radio::V1_6::RadioError::RADIO_NOT_AVAILABLE,
+            ::android::hardware::radio::V1_6::RadioError::NO_MEMORY,
+            ::android::hardware::radio::V1_6::RadioError::INTERNAL_ERR,
+            ::android::hardware::radio::V1_6::RadioError::SYSTEM_ERR,
+            ::android::hardware::radio::V1_6::RadioError::REQUEST_NOT_SUPPORTED,
+            ::android::hardware::radio::V1_6::RadioError::CANCELLED};
+    if (flag == CHECK_GENERAL_ERROR || flag == CHECK_OEM_AND_GENERAL_ERROR) {
+        for (size_t i = 0; i < generalErrors.size(); i++) {
+            if (err == generalErrors[i]) {
+                return testing::AssertionSuccess();
+            }
+        }
+    }
+    if (flag == CHECK_OEM_ERROR || flag == CHECK_OEM_AND_GENERAL_ERROR) {
+        if (err >= ::android::hardware::radio::V1_6::RadioError::OEM_ERROR_1 &&
+            err <= ::android::hardware::radio::V1_6::RadioError::OEM_ERROR_25) {
+            return testing::AssertionSuccess();
+        }
+    }
+    for (size_t i = 0; i < errors.size(); i++) {
+        if (err == errors[i]) {
+            return testing::AssertionSuccess();
+        }
+    }
+    return testing::AssertionFailure() << "RadioError:" + toString(err) + " is returned";
+}
diff --git a/radio/1.6/vts/functional/vts_test_util_v1_6.h b/radio/1.6/vts/functional/vts_test_util_v1_6.h
new file mode 100644
index 0000000..e04d201
--- /dev/null
+++ b/radio/1.6/vts/functional/vts_test_util_v1_6.h
@@ -0,0 +1,28 @@
+/*
+ * 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/hardware/radio/1.6/types.h>
+
+#include "vts_test_util.h"
+
+/*
+ * Check multiple radio error codes which are possibly returned because of the different
+ * vendor/devices implementations. It allows optional checks for general errors or/and oem errors.
+ */
+::testing::AssertionResult CheckAnyOfErrors(
+        ::android::hardware::radio::V1_6::RadioError err,
+        std::vector<::android::hardware::radio::V1_6::RadioError> generalError,
+        CheckFlag flag = CHECK_DEFAULT);
diff --git a/radio/config/1.0/Android.bp b/radio/config/1.0/Android.bp
index 387f953..9e317b3 100644
--- a/radio/config/1.0/Android.bp
+++ b/radio/config/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio.config@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadioConfig.hal",
diff --git a/radio/config/1.0/default/Android.bp b/radio/config/1.0/default/Android.bp
index a0f4214..e221ceb 100644
--- a/radio/config/1.0/default/Android.bp
+++ b/radio/config/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.radio.config@1.0-service",
     init_rc: ["android.hardware.radio.config@1.0-service.rc"],
diff --git a/radio/config/1.0/vts/functional/Android.bp b/radio/config/1.0/vts/functional/Android.bp
index 330209e..36aecff 100644
--- a/radio/config/1.0/vts/functional/Android.bp
+++ b/radio/config/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioConfigV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -29,5 +38,8 @@
         "android.hardware.radio.config@1.0",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp b/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
index b3fae86..648b63a 100644
--- a/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
+++ b/radio/config/1.0/vts/functional/VtsHalRadioConfigV1_0TargetTest.cpp
@@ -16,6 +16,7 @@
 
 #include <radio_config_hidl_hal_utils.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, RadioConfigHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
diff --git a/tests/bar/1.0/.hidl_for_test b/radio/config/1.1/.hidl_for_system_ext
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to radio/config/1.1/.hidl_for_system_ext
diff --git a/radio/config/1.1/Android.bp b/radio/config/1.1/Android.bp
index 1e9071a..b1705f9 100644
--- a/radio/config/1.1/Android.bp
+++ b/radio/config/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio.config@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadioConfig.hal",
@@ -18,4 +24,5 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/radio/config/1.1/vts/functional/Android.bp b/radio/config/1.1/vts/functional/Android.bp
index f60331d..9037b79 100644
--- a/radio/config/1.1/vts/functional/Android.bp
+++ b/radio/config/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioConfigV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -29,5 +38,8 @@
         "android.hardware.radio.config@1.1",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp b/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
index b3fae86..648b63a 100644
--- a/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
+++ b/radio/config/1.1/vts/functional/VtsHalRadioConfigV1_1TargetTest.cpp
@@ -16,6 +16,7 @@
 
 #include <radio_config_hidl_hal_utils.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, RadioConfigHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
diff --git a/tests/bar/1.0/.hidl_for_test b/radio/config/1.2/.hidl_for_system_ext
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to radio/config/1.2/.hidl_for_system_ext
diff --git a/radio/config/1.2/Android.bp b/radio/config/1.2/Android.bp
index 812f166..3327af4 100644
--- a/radio/config/1.2/Android.bp
+++ b/radio/config/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio.config@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IRadioConfigIndication.hal",
@@ -18,4 +24,5 @@
         "android.hidl.base@1.0",
     ],
     gen_java: true,
+    system_ext_specific: true,
 }
diff --git a/radio/config/1.2/vts/functional/Android.bp b/radio/config/1.2/vts/functional/Android.bp
index fdc83b7..1a15d3f 100644
--- a/radio/config/1.2/vts/functional/Android.bp
+++ b/radio/config/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRadioConfigV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -31,5 +40,8 @@
         "android.hardware.radio.config@1.2",
     ],
     header_libs: ["radio.util.header@1.0"],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
 }
diff --git a/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp b/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
index f09ac3a..c92ef69 100644
--- a/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
+++ b/radio/config/1.2/vts/functional/VtsHalRadioConfigV1_2TargetTest.cpp
@@ -16,6 +16,7 @@
 
 #include <radio_config_hidl_hal_utils.h>
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, RadioConfigHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
diff --git a/radio/config/1.3/Android.bp b/radio/config/1.3/Android.bp
new file mode 100644
index 0000000..dc0d82c
--- /dev/null
+++ b/radio/config/1.3/Android.bp
@@ -0,0 +1,29 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.radio.config@1.3",
+    root: "android.hardware",
+    srcs: [
+        "IRadioConfig.hal",
+        "IRadioConfigResponse.hal",
+    ],
+    interfaces: [
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio@1.0",
+        "android.hardware.radio@1.6",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+    system_ext_specific: true,
+}
diff --git a/radio/config/1.3/IRadioConfig.hal b/radio/config/1.3/IRadioConfig.hal
new file mode 100644
index 0000000..83bcf92
--- /dev/null
+++ b/radio/config/1.3/IRadioConfig.hal
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ *
+ *
+ * This interface is used by telephony and telecom to talk to cellular radio for the purpose of
+ * radio configuration, and it is not associated with any specific modem or slot.
+ * All the functions have minimum one parameter:
+ * serial: which corresponds to serial no. of request. Serial numbers must only be memorized for the
+ * duration of a method call. If clients provide colliding serials (including passing the same
+ * serial to different methods), multiple responses (one for each method call) must still be served.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import @1.1::IRadioConfig;
+import IRadioConfigResponse;
+
+interface IRadioConfig extends @1.1::IRadioConfig {
+    /**
+     * Gets the available Radio Hal capabilities on the current device.
+     *
+     * This is called once per device boot up.
+     *
+     * @param serial Serial number of request
+     *
+     * Response callback is
+     * IRadioConfigResponse.getHalDeviceCapabilitiesResponse()
+     */
+    oneway getHalDeviceCapabilities(int32_t serial);
+};
diff --git a/radio/config/1.3/IRadioConfigResponse.hal b/radio/config/1.3/IRadioConfigResponse.hal
new file mode 100644
index 0000000..f6aee31
--- /dev/null
+++ b/radio/config/1.3/IRadioConfigResponse.hal
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.3;
+
+import android.hardware.radio@1.6::RadioResponseInfo;
+import @1.2::IRadioConfigResponse;
+
+/**
+ * Interface declaring response functions to solicited radio config requests.
+ */
+interface IRadioConfigResponse extends @1.2::IRadioConfigResponse {
+    /**
+     * @param info Response info struct containing response type, serial no. and error
+     * @param modemReducedFeatureSet1 True indicates that the modem does NOT support the following
+     *        features.
+     *        - Providing either
+     *          android.hardware.radio@1.6::LinkCapacityEstimate:secondaryDownlinkCapacityKbps
+     *          or android.hardware.radio@1.6::LinkCapacityEstimate:secondaryUplinkCapacityKbps
+     *          when given from
+     *          android.hardware.radio@1.6::RadioIndication:currentLinkCapacityEstimate
+     *        - Calling android.hardware.radio@1.6::IRadio.setNrDualConnectivityState
+     *          or querying android.hardware.radio@1.6::IRadio.isNrDualConnectivityEnabled
+     *        - Requesting android.hardware.radio@1.6::IRadio.setDataThrottling()
+     *        - Providing android.hardware.radio@1.6::SlicingConfig through
+     *          android.hardware.radio@1.6::getSlicingConfig()
+     *        - Providing android.hardware.radio@1.6::PhysicalChannelConfig through
+     *          android.hardware.radio@1.6::IRadioIndication.currentPhysicalChannelConfigs_1_6()
+     *
+     * Valid errors returned:
+     *   RadioError:NONE
+     *   RadioError:RADIO_NOT_AVAILABLE
+     *   RadioError:INTERNAL_ERR
+     */
+    oneway getHalDeviceCapabilitiesResponse(RadioResponseInfo info,
+        bool modemReducedFeatureSet1);
+};
diff --git a/radio/config/1.3/types.hal b/radio/config/1.3/types.hal
new file mode 100644
index 0000000..2b6c9f0
--- /dev/null
+++ b/radio/config/1.3/types.hal
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.hardware.radio.config@1.3;
+
+/**
+ * Contains the device capabilities with respect to the Radio HAL.
+ */
+struct HalDeviceCapabilities {
+  /**
+   * True indicates that the modem does NOT support the following features:
+   * <ul>
+   * <li>Providing either
+   * android.hardware.radio@1.6::LinkCapacityEstimate:secondaryDownlinkCapacityKbps
+   * or android.hardware.radio@1.6::LinkCapacityEstimate:secondaryUplinkCapacityKbps
+   * when given from android.hardware.radio@1.6::RadioIndication:currentLinkCapacityEstimate
+   * </li>
+   * <li> calling android.hardware.radio@1.6::IRadio.setNrDualConnectivityState
+   * or querying android.hardware.radio@1.6::IRadio.isNrDualConnectivityEnabled
+   * </li>
+   * <li>Requesting android.hardware.radio@1.6::IRadio.setDataThrottling()
+   * </li>
+   * </ul>
+   */
+  bool modemReducedFeatureSet1;
+};
diff --git a/radio/config/1.3/vts/functional/Android.bp b/radio/config/1.3/vts/functional/Android.bp
new file mode 100644
index 0000000..20c480f
--- /dev/null
+++ b/radio/config/1.3/vts/functional/Android.bp
@@ -0,0 +1,71 @@
+//
+// Copyright (C) 2019 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalRadioConfigV1_3TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: [
+        "radio_config_hidl_hal_api.cpp",
+        "radio_config_hidl_hal_test.cpp",
+        "radio_config_response.cpp",
+        "radio_config_indication.cpp",
+        "VtsHalRadioConfigV1_3TargetTest.cpp",
+    ],
+    static_libs: [
+        "RadioVtsTestUtilBase",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio.config@1.3",
+    ],
+    header_libs: ["radio.util.header@1.0"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_library_static {
+    name: "RadioConfigVtsTestResponse",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs : [
+        "radio_config_response.cpp",
+        "radio_config_hidl_hal_test.cpp",
+    ],
+    header_libs: ["radio.util.header@1.0"],
+    static_libs: ["RadioVtsTestUtilBase"],
+    shared_libs: [
+        "android.hardware.radio@1.0",
+        "android.hardware.radio.config@1.0",
+        "android.hardware.radio.config@1.1",
+        "android.hardware.radio.config@1.2",
+        "android.hardware.radio.config@1.3",
+    ],
+}
+
+cc_library_headers {
+    name: "radio.config.util.header@1.3",
+    export_include_dirs: ["."],
+}
diff --git a/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
new file mode 100644
index 0000000..5772d08
--- /dev/null
+++ b/radio/config/1.3/vts/functional/VtsHalRadioConfigV1_3TargetTest.cpp
@@ -0,0 +1,23 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RadioConfigHidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, RadioConfigHidlTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IRadioConfig::descriptor)),
+        android::hardware::PrintInstanceNameToString);
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
new file mode 100644
index 0000000..8df02dd
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_api.cpp
@@ -0,0 +1,30 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+#define ASSERT_OK(ret) ASSERT_TRUE(ret.isOk())
+
+/*
+ * Test IRadioConfig.getHalDeviceCapabilities()
+ */
+TEST_P(RadioConfigHidlTest, getHalDeviceCapabilities) {
+    const int serial = GetRandomSerialNumber();
+    Return<void> res = radioConfig->getHalDeviceCapabilities(serial);
+    ASSERT_OK(res);
+    ALOGI("getHalDeviceCapabilities, rspInfo.error = %s\n",
+          toString(radioConfigRsp->rspInfo.error).c_str());
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
new file mode 100644
index 0000000..da61464
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_test.cpp
@@ -0,0 +1,33 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+void RadioConfigHidlTest::SetUp() {
+    radioConfig = IRadioConfig::getService(GetParam());
+    if (radioConfig == NULL) {
+        sleep(60);
+        radioConfig = IRadioConfig::getService(GetParam());
+    }
+    ASSERT_NE(nullptr, radioConfig.get());
+
+    radioConfigRsp = new (std::nothrow) RadioConfigResponse(*this);
+    ASSERT_NE(nullptr, radioConfigRsp.get());
+
+    count_ = 0;
+
+    radioConfig->setResponseFunctions(radioConfigRsp, nullptr);
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
new file mode 100644
index 0000000..7440054
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_hidl_hal_utils.h
@@ -0,0 +1,122 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <android-base/logging.h>
+
+#include <chrono>
+#include <condition_variable>
+#include <mutex>
+
+#include <android/hardware/radio/config/1.1/IRadioConfig.h>
+#include <android/hardware/radio/config/1.1/types.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigIndication.h>
+#include <android/hardware/radio/config/1.2/IRadioConfigResponse.h>
+#include <android/hardware/radio/config/1.2/types.h>
+#include <android/hardware/radio/config/1.3/IRadioConfig.h>
+#include <android/hardware/radio/config/1.3/IRadioConfigResponse.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+#include <log/log.h>
+
+#include "vts_test_util.h"
+
+using namespace ::android::hardware::radio::config::V1_2;
+
+using ::android::sp;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::Void;
+using ::android::hardware::radio::config::V1_1::ModemsConfig;
+using ::android::hardware::radio::config::V1_1::PhoneCapability;
+using ::android::hardware::radio::config::V1_2::SimSlotStatus;
+using ::android::hardware::radio::config::V1_3::IRadioConfig;
+using ::android::hardware::radio::V1_0::RadioResponseInfo;
+
+#define RADIO_SERVICE_NAME "slot1"
+
+class RadioConfigHidlTest;
+
+/* Callback class for radio config response */
+class RadioConfigResponse : public IRadioConfigResponse {
+  protected:
+    RadioResponseWaiter& parent;
+
+  public:
+    RadioResponseInfo rspInfo;
+    PhoneCapability phoneCap;
+    bool modemReducedFeatureSet1;
+
+    RadioConfigResponse(RadioResponseWaiter& parent);
+    virtual ~RadioConfigResponse() = default;
+
+    Return<void> getSimSlotsStatusResponse(
+            const RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<
+                    ::android::hardware::radio::config::V1_0::SimSlotStatus>& slotStatus);
+
+    Return<void> getSimSlotsStatusResponse_1_2(
+            const RadioResponseInfo& info,
+            const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+
+    Return<void> setSimSlotsMappingResponse(const RadioResponseInfo& info);
+
+    Return<void> getPhoneCapabilityResponse(const RadioResponseInfo& info,
+                                            const PhoneCapability& phoneCapability);
+
+    Return<void> setPreferredDataModemResponse(const RadioResponseInfo& info);
+
+    Return<void> getModemsConfigResponse(const RadioResponseInfo& info,
+                                         const ModemsConfig& mConfig);
+
+    Return<void> setModemsConfigResponse(const RadioResponseInfo& info);
+
+    Return<void> getHalDeviceCapabilitiesResponse(
+            const ::android::hardware::radio::V1_6::RadioResponseInfo& info,
+            bool modemReducedFeatureSet1);
+};
+
+/* Callback class for radio config indication */
+class RadioConfigIndication : public IRadioConfigIndication {
+  protected:
+    RadioConfigHidlTest& parent;
+
+  public:
+    RadioConfigIndication(RadioConfigHidlTest& parent);
+    virtual ~RadioConfigIndication() = default;
+
+    Return<void> simSlotsStatusChanged_1_2(
+            ::android::hardware::radio::V1_0::RadioIndicationType type,
+            const ::android::hardware::hidl_vec<SimSlotStatus>& slotStatus);
+};
+
+// The main test class for Radio config HIDL.
+class RadioConfigHidlTest : public ::testing::TestWithParam<std::string>,
+                            public RadioResponseWaiter {
+  public:
+    virtual void SetUp() override;
+
+    void updateSimCardStatus();
+
+    /* radio config service handle */
+    sp<IRadioConfig> radioConfig;
+
+    /* radio config response handle */
+    sp<RadioConfigResponse> radioConfigRsp;
+};
diff --git a/radio/config/1.3/vts/functional/radio_config_indication.cpp b/radio/config/1.3/vts/functional/radio_config_indication.cpp
new file mode 100644
index 0000000..6fa443c
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_indication.cpp
@@ -0,0 +1,25 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+RadioConfigIndication::RadioConfigIndication(RadioConfigHidlTest& parent) : parent(parent) {}
+
+Return<void> RadioConfigIndication::simSlotsStatusChanged_1_2(
+        ::android::hardware::radio::V1_0::RadioIndicationType /*type*/,
+        const ::android::hardware::hidl_vec<SimSlotStatus>& /*slotStatus*/) {
+    return Void();
+}
diff --git a/radio/config/1.3/vts/functional/radio_config_response.cpp b/radio/config/1.3/vts/functional/radio_config_response.cpp
new file mode 100644
index 0000000..6b2d27c
--- /dev/null
+++ b/radio/config/1.3/vts/functional/radio_config_response.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <radio_config_hidl_hal_utils.h>
+
+// SimSlotStatus slotStatus;
+
+RadioConfigResponse::RadioConfigResponse(RadioResponseWaiter& parent) : parent(parent) {}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ::android::hardware::hidl_vec<
+                ::android::hardware::radio::config::V1_0::SimSlotStatus>& /* slotStatus */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getSimSlotsStatusResponse_1_2(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ::android::hardware::hidl_vec<SimSlotStatus>& /* slotStatus */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setSimSlotsMappingResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getPhoneCapabilityResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& info,
+        const PhoneCapability& phoneCapability) {
+    rspInfo = info;
+    phoneCap = phoneCapability;
+    parent.notify(info.serial);
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setPreferredDataModemResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getModemsConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */,
+        const ModemsConfig& /* mConfig */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::setModemsConfigResponse(
+        const ::android::hardware::radio::V1_0::RadioResponseInfo& /* info */) {
+    return Void();
+}
+
+Return<void> RadioConfigResponse::getHalDeviceCapabilitiesResponse(
+        const ::android::hardware::radio::V1_6::RadioResponseInfo& /* info */,
+        bool modemReducedFeatures) {
+    modemReducedFeatureSet1 = modemReducedFeatures;
+    return Void();
+}
diff --git a/radio/deprecated/1.0/Android.bp b/radio/deprecated/1.0/Android.bp
index cb13b86..53f6da5 100644
--- a/radio/deprecated/1.0/Android.bp
+++ b/radio/deprecated/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.radio.deprecated@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IOemHook.hal",
         "IOemHookIndication.hal",
diff --git a/rebootescrow/aidl/Android.bp b/rebootescrow/aidl/Android.bp
index 75faa1a..c764f86 100644
--- a/rebootescrow/aidl/Android.bp
+++ b/rebootescrow/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.rebootescrow",
     vendor_available: true,
diff --git a/rebootescrow/aidl/default/Android.bp b/rebootescrow/aidl/default/Android.bp
index b77272f..1f67a3e 100644
--- a/rebootescrow/aidl/default/Android.bp
+++ b/rebootescrow/aidl/default/Android.bp
@@ -14,13 +14,22 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "librebootescrowdefaultimpl",
     vendor: true,
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.rebootescrow-ndk_platform",
+        "android.hardware.rebootescrow-V1-ndk_platform",
     ],
     export_include_dirs: ["include"],
     srcs: [
@@ -47,7 +56,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.rebootescrow-ndk_platform",
+        "android.hardware.rebootescrow-V1-ndk_platform",
     ],
     static_libs: [
         "libhadamardutils",
@@ -78,7 +87,9 @@
     ],
     static_libs: [
         "libhadamardutils",
-        "libgtest_prod",
+    ],
+    header_libs: [
+        "libgtest_prod_headers",
     ],
     shared_libs: [
         "liblog",
diff --git a/rebootescrow/aidl/default/RebootEscrow.cpp b/rebootescrow/aidl/default/RebootEscrow.cpp
index dbc0921..8e5e97c 100644
--- a/rebootescrow/aidl/default/RebootEscrow.cpp
+++ b/rebootescrow/aidl/default/RebootEscrow.cpp
@@ -28,7 +28,7 @@
 
 using ::android::base::unique_fd;
 
-ndk::ScopedAStatus RebootEscrow::storeKey(const std::vector<int8_t>& kek) {
+ndk::ScopedAStatus RebootEscrow::storeKey(const std::vector<uint8_t>& ukek) {
     int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_WRONLY | O_NOFOLLOW | O_CLOEXEC));
     unique_fd fd(rawFd);
     if (fd.get() < 0) {
@@ -36,7 +36,6 @@
         return ndk::ScopedAStatus(AStatus_fromExceptionCode(EX_UNSUPPORTED_OPERATION));
     }
 
-    std::vector<uint8_t> ukek(kek.begin(), kek.end());
     auto encoded = hadamard::EncodeKey(ukek);
 
     if (!::android::base::WriteFully(fd, encoded.data(), encoded.size())) {
@@ -47,7 +46,7 @@
     return ndk::ScopedAStatus::ok();
 }
 
-ndk::ScopedAStatus RebootEscrow::retrieveKey(std::vector<int8_t>* _aidl_return) {
+ndk::ScopedAStatus RebootEscrow::retrieveKey(std::vector<uint8_t>* _aidl_return) {
     int rawFd = TEMP_FAILURE_RETRY(::open(devicePath_.c_str(), O_RDONLY | O_NOFOLLOW | O_CLOEXEC));
     unique_fd fd(rawFd);
     if (fd.get() < 0) {
@@ -63,8 +62,7 @@
 
     auto keyBytes = hadamard::DecodeKey(encodedBytes);
 
-    std::vector<int8_t> signedKeyBytes(keyBytes.begin(), keyBytes.end());
-    *_aidl_return = signedKeyBytes;
+    *_aidl_return = keyBytes;
     return ndk::ScopedAStatus::ok();
 }
 
diff --git a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
index 00ff16b..cdbeb67 100644
--- a/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
+++ b/rebootescrow/aidl/default/include/rebootescrow-impl/RebootEscrow.h
@@ -26,8 +26,8 @@
 class RebootEscrow : public BnRebootEscrow {
   public:
     explicit RebootEscrow(const std::string& devicePath) : devicePath_(devicePath) {}
-    ndk::ScopedAStatus storeKey(const std::vector<int8_t>& kek) override;
-    ndk::ScopedAStatus retrieveKey(std::vector<int8_t>* _aidl_return) override;
+    ndk::ScopedAStatus storeKey(const std::vector<uint8_t>& kek) override;
+    ndk::ScopedAStatus retrieveKey(std::vector<uint8_t>* _aidl_return) override;
 
   private:
     const std::string devicePath_;
diff --git a/rebootescrow/aidl/vts/functional/Android.bp b/rebootescrow/aidl/vts/functional/Android.bp
index 2cc0068..76b6784 100644
--- a/rebootescrow/aidl/vts/functional/Android.bp
+++ b/rebootescrow/aidl/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRebootEscrowTargetTest",
     defaults: [
@@ -25,7 +34,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.rebootescrow-cpp",
+        "android.hardware.rebootescrow-V1-cpp",
     ],
     test_suites: [
         "vts",
diff --git a/renderscript/1.0/Android.bp b/renderscript/1.0/Android.bp
index d3b5abe..1f2ac15 100644
--- a/renderscript/1.0/Android.bp
+++ b/renderscript/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.renderscript@1.0",
     root: "android.hardware",
diff --git a/renderscript/1.0/default/Android.bp b/renderscript/1.0/default/Android.bp
index 4fa85c6..c68e370 100644
--- a/renderscript/1.0/default/Android.bp
+++ b/renderscript/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.renderscript@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/renderscript/1.0/types.hal b/renderscript/1.0/types.hal
index 7c32188..bb39baa 100644
--- a/renderscript/1.0/types.hal
+++ b/renderscript/1.0/types.hal
@@ -170,7 +170,6 @@
 };
 
 // Script to Script
-@export(name="RsScriptCall")
 struct ScriptCall {
     ForEachStrategy strategy;
     uint32_t        xStart;
diff --git a/renderscript/1.0/vts/functional/Android.bp b/renderscript/1.0/vts/functional/Android.bp
index 327c09e..4b665b1 100644
--- a/renderscript/1.0/vts/functional/Android.bp
+++ b/renderscript/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalRenderscriptV1_0TargetTest",
     srcs: [
diff --git a/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.cpp b/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.cpp
index 5e5776c..ddd99fb 100644
--- a/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.cpp
+++ b/renderscript/1.0/vts/functional/VtsHalRenderscriptV1_0TargetTest.cpp
@@ -34,6 +34,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(RenderscriptHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, RenderscriptHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IDevice::descriptor)),
diff --git a/scripts/list_hal_vts.py b/scripts/list_hal_vts.py
new file mode 100755
index 0000000..1fb51a5
--- /dev/null
+++ b/scripts/list_hal_vts.py
@@ -0,0 +1,143 @@
+#!/usr/bin/python3
+
+"""
+List VTS tests for each HAL by parsing module-info.json.
+
+Example usage:
+
+  # First, build modules-info.json
+  m -j "${ANDROID_PRODUCT_OUT#$ANDROID_BUILD_TOP/}/module-info.json"
+
+  # List with pretty-printed JSON. *IDL packages without a VTS module will show up
+  # as keys with empty lists.
+  ./list_hals_vts.py | python3 -m json.tool
+
+  # List with CSV. *IDL packages without a VTS module will show up as a line with
+  # empty value in the VTS module column.
+  ./list_hals_vts.py --csv
+"""
+
+import argparse
+import collections
+import csv
+import io
+import json
+import os
+import logging
+import pathlib
+import re
+import sys
+
+PATH_PACKAGE_PATTERN = re.compile(
+  r'^hardware/interfaces/(?P<path>(?:\w+/)*?)(?:aidl|(?P<version>\d+\.\d+))/.*')
+
+
+class CriticalHandler(logging.StreamHandler):
+  def emit(self, record):
+    super(CriticalHandler, self).emit(record)
+    if record.levelno >= logging.CRITICAL:
+      sys.exit(1)
+
+
+logger = logging.getLogger(__name__)
+logger.addHandler(CriticalHandler())
+
+
+def default_json():
+  out = os.environ.get('ANDROID_PRODUCT_OUT')
+  if not out: return None
+  return os.path.join(out, 'module-info.json')
+
+
+def infer_package(path):
+  """
+  Infer package from a relative path from build top where a VTS module lives.
+
+  :param path: a path like 'hardware/interfaces/vibrator/aidl/vts'
+  :return: The inferred *IDL package, e.g. 'android.hardware.vibrator'
+
+  >>> infer_package('hardware/interfaces/automotive/sv/1.0/vts/functional')
+  'android.hardware.automotive.sv@1.0'
+  >>> infer_package('hardware/interfaces/vibrator/aidl/vts')
+  'android.hardware.vibrator'
+  """
+  mo = re.match(PATH_PACKAGE_PATTERN, path)
+  if not mo: return None
+  package = 'android.hardware.' + ('.'.join(pathlib.Path(mo.group('path')).parts))
+  if mo.group('version'):
+    package += '@' + mo.group('version')
+  return package
+
+
+def load_modules_info(json_file):
+  """
+  :param json_file: The path to modules-info.json
+  :return: a dictionary, where the keys are inferred *IDL package names, and
+           values are a list of VTS modules with that inferred package name.
+  """
+  with open(json_file) as fp:
+    root = json.load(fp)
+    ret = collections.defaultdict(list)
+    for module_name, module_info in root.items():
+      if 'vts' not in module_info.get('compatibility_suites', []):
+        continue
+      for path in module_info.get('path', []):
+        inferred_package = infer_package(path)
+        if not inferred_package:
+          continue
+        ret[inferred_package].append(module_name)
+    return ret
+
+
+def add_missing_idl(vts_modules):
+  top = os.environ.get("ANDROID_BUILD_TOP")
+  interfaces = None
+  if top:
+    interfaces = os.path.join(top, "hardware", "interfaces")
+  else:
+    logger.warning("Missing ANDROID_BUILD_TOP")
+    interfaces = "hardware/interfaces"
+  if not os.path.isdir(interfaces):
+    logger.error("Not adding missing *IDL modules because missing hardware/interfaces dir")
+    return
+  assert not interfaces.endswith(os.path.sep)
+  for root, dirs, files in os.walk(interfaces):
+    for dir in dirs:
+      full_dir = os.path.join(root, dir)
+      assert full_dir.startswith(interfaces)
+      top_rel_dir = os.path.join('hardware', 'interfaces', full_dir[len(interfaces) + 1:])
+      inferred_package = infer_package(top_rel_dir)
+      if inferred_package is None:
+        continue
+      if inferred_package not in vts_modules:
+        vts_modules[inferred_package] = []
+
+
+def main():
+  parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
+  parser.add_argument('json', metavar='module-info.json', default=default_json(), nargs='?')
+  parser.add_argument('--csv', action='store_true', help='Print CSV. If not specified print JSON.')
+  args = parser.parse_args()
+  if not args.json:
+    logger.critical('No module-info.json is specified or found.')
+  vts_modules = load_modules_info(args.json)
+  add_missing_idl(vts_modules)
+
+  if args.csv:
+    out = io.StringIO()
+    writer = csv.writer(out, )
+    writer.writerow(["package", "vts_module"])
+    for package, modules in vts_modules.items():
+      if not modules:
+        writer.writerow([package, ""])
+      for module in modules:
+        writer.writerow([package, module])
+    result = out.getvalue()
+  else:
+    result = json.dumps(vts_modules)
+
+  print(result)
+
+
+if __name__ == '__main__':
+  main()
diff --git a/secure_element/1.0/Android.bp b/secure_element/1.0/Android.bp
index 32b752b..520d3a3 100644
--- a/secure_element/1.0/Android.bp
+++ b/secure_element/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.secure_element@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISecureElement.hal",
diff --git a/secure_element/1.0/vts/functional/Android.bp b/secure_element/1.0/vts/functional/Android.bp
index d428c6f..735d7c9 100644
--- a/secure_element/1.0/vts/functional/Android.bp
+++ b/secure_element/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSecureElementV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/secure_element/1.0/vts/functional/AndroidTest.xml b/secure_element/1.0/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..4724f23
--- /dev/null
+++ b/secure_element/1.0/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalSecureElementV1_0TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+    <option name="config-descriptor:metadata" key="token" value="SECURE_ELEMENT_SIM_CARD" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalSecureElementV1_0TargetTest->/data/local/tmp/VtsHalSecureElementV1_0TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalSecureElementV1_0TargetTest" />
+    </test>
+</configuration>
diff --git a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
index 93ffd05..63c2eca 100644
--- a/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
+++ b/secure_element/1.0/vts/functional/VtsHalSecureElementV1_0TargetTest.cpp
@@ -204,6 +204,7 @@
     EXPECT_EQ(SecureElementStatus::SUCCESS, se_->closeChannel(response.channelNumber));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SecureElementHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
diff --git a/secure_element/1.1/Android.bp b/secure_element/1.1/Android.bp
index 3ea2de9..3874fda 100644
--- a/secure_element/1.1/Android.bp
+++ b/secure_element/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.secure_element@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ISecureElement.hal",
         "ISecureElementHalCallback.hal",
diff --git a/secure_element/1.1/vts/functional/Android.bp b/secure_element/1.1/vts/functional/Android.bp
index 200aed8..d63e7c4 100644
--- a/secure_element/1.1/vts/functional/Android.bp
+++ b/secure_element/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSecureElementV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/secure_element/1.1/vts/functional/AndroidTest.xml b/secure_element/1.1/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..3d7b715
--- /dev/null
+++ b/secure_element/1.1/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalSecureElementV1_1TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+    <option name="config-descriptor:metadata" key="token" value="SECURE_ELEMENT_SIM_CARD" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalSecureElementV1_1TargetTest->/data/local/tmp/VtsHalSecureElementV1_1TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalSecureElementV1_1TargetTest" />
+    </test>
+</configuration>
diff --git a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
index 66fc47c..234c33c 100644
--- a/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
+++ b/secure_element/1.1/vts/functional/VtsHalSecureElementV1_1TargetTest.cpp
@@ -88,6 +88,7 @@
     EXPECT_TRUE(se_->isCardPresent());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SecureElementHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
diff --git a/secure_element/1.2/Android.bp b/secure_element/1.2/Android.bp
index e134771..6de92df 100644
--- a/secure_element/1.2/Android.bp
+++ b/secure_element/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.secure_element@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ISecureElement.hal",
     ],
diff --git a/secure_element/1.2/vts/functional/Android.bp b/secure_element/1.2/vts/functional/Android.bp
index 9a7ca45..63a0a19 100644
--- a/secure_element/1.2/vts/functional/Android.bp
+++ b/secure_element/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSecureElementV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/secure_element/1.2/vts/functional/AndroidTest.xml b/secure_element/1.2/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..0bd8061
--- /dev/null
+++ b/secure_element/1.2/vts/functional/AndroidTest.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsHalSecureElementV1_2TargetTest.">
+    <option name="test-suite-tag" value="apct" />
+    <option name="test-suite-tag" value="apct-native" />
+    <option name="config-descriptor:metadata" key="token" value="SECURE_ELEMENT_SIM_CARD" />
+
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
+    </target_preparer>
+
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="VtsHalSecureElementV1_2TargetTest->/data/local/tmp/VtsHalSecureElementV1_2TargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsHalSecureElementV1_2TargetTest" />
+    </test>
+</configuration>
diff --git a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
index 9392f14..66d581e 100644
--- a/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
+++ b/secure_element/1.2/vts/functional/VtsHalSecureElementV1_2TargetTest.cpp
@@ -100,6 +100,7 @@
     EXPECT_TRUE(res.args->state_);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureElementHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SecureElementHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISecureElement::descriptor)),
diff --git a/security/keymint/aidl/Android.bp b/security/keymint/aidl/Android.bp
new file mode 100644
index 0000000..3796847
--- /dev/null
+++ b/security/keymint/aidl/Android.bp
@@ -0,0 +1,39 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.keymint",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/security/keymint/*.aidl",
+    ],
+    imports: [
+        "android.hardware.security.secureclock",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+            apps_enabled: false,
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.compos",
+            ],
+        },
+    },
+}
diff --git a/security/keymint/aidl/OWNERS b/security/keymint/aidl/OWNERS
new file mode 100644
index 0000000..a93b171
--- /dev/null
+++ b/security/keymint/aidl/OWNERS
@@ -0,0 +1,4 @@
+jbires@google.com
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
new file mode 100644
index 0000000..6da124f
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Algorithm.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Algorithm {
+  RSA = 1,
+  EC = 3,
+  AES = 32,
+  TRIPLE_DES = 33,
+  HMAC = 128,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
new file mode 100644
index 0000000..90f2e6e
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/AttestationKey.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable AttestationKey {
+  byte[] keyBlob;
+  android.hardware.security.keymint.KeyParameter[] attestKeyParams;
+  byte[] issuerSubjectName;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
new file mode 100644
index 0000000..c952a31
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BeginResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable BeginResult {
+  long challenge;
+  android.hardware.security.keymint.KeyParameter[] params;
+  android.hardware.security.keymint.IKeyMintOperation operation;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
new file mode 100644
index 0000000..0049883
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/BlockMode.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum BlockMode {
+  ECB = 1,
+  CBC = 2,
+  CTR = 3,
+  GCM = 32,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
new file mode 100644
index 0000000..645f0a7
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Certificate.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable Certificate {
+  byte[] encodedCertificate;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
new file mode 100644
index 0000000..d04d49c
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/DeviceInfo.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable DeviceInfo {
+  byte[] deviceInfo;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
new file mode 100644
index 0000000..0df7096
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Digest.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Digest {
+  NONE = 0,
+  MD5 = 1,
+  SHA1 = 2,
+  SHA_2_224 = 3,
+  SHA_2_256 = 4,
+  SHA_2_384 = 5,
+  SHA_2_512 = 6,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
new file mode 100644
index 0000000..6b4a9ae
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/EcCurve.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum EcCurve {
+  P_224 = 0,
+  P_256 = 1,
+  P_384 = 2,
+  P_521 = 3,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
new file mode 100644
index 0000000..b05a0f3
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ErrorCode.aidl
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum ErrorCode {
+  OK = 0,
+  ROOT_OF_TRUST_ALREADY_SET = -1,
+  UNSUPPORTED_PURPOSE = -2,
+  INCOMPATIBLE_PURPOSE = -3,
+  UNSUPPORTED_ALGORITHM = -4,
+  INCOMPATIBLE_ALGORITHM = -5,
+  UNSUPPORTED_KEY_SIZE = -6,
+  UNSUPPORTED_BLOCK_MODE = -7,
+  INCOMPATIBLE_BLOCK_MODE = -8,
+  UNSUPPORTED_MAC_LENGTH = -9,
+  UNSUPPORTED_PADDING_MODE = -10,
+  INCOMPATIBLE_PADDING_MODE = -11,
+  UNSUPPORTED_DIGEST = -12,
+  INCOMPATIBLE_DIGEST = -13,
+  INVALID_EXPIRATION_TIME = -14,
+  INVALID_USER_ID = -15,
+  INVALID_AUTHORIZATION_TIMEOUT = -16,
+  UNSUPPORTED_KEY_FORMAT = -17,
+  INCOMPATIBLE_KEY_FORMAT = -18,
+  UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19,
+  UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20,
+  INVALID_INPUT_LENGTH = -21,
+  KEY_EXPORT_OPTIONS_INVALID = -22,
+  DELEGATION_NOT_ALLOWED = -23,
+  KEY_NOT_YET_VALID = -24,
+  KEY_EXPIRED = -25,
+  KEY_USER_NOT_AUTHENTICATED = -26,
+  OUTPUT_PARAMETER_NULL = -27,
+  INVALID_OPERATION_HANDLE = -28,
+  INSUFFICIENT_BUFFER_SPACE = -29,
+  VERIFICATION_FAILED = -30,
+  TOO_MANY_OPERATIONS = -31,
+  UNEXPECTED_NULL_POINTER = -32,
+  INVALID_KEY_BLOB = -33,
+  IMPORTED_KEY_NOT_ENCRYPTED = -34,
+  IMPORTED_KEY_DECRYPTION_FAILED = -35,
+  IMPORTED_KEY_NOT_SIGNED = -36,
+  IMPORTED_KEY_VERIFICATION_FAILED = -37,
+  INVALID_ARGUMENT = -38,
+  UNSUPPORTED_TAG = -39,
+  INVALID_TAG = -40,
+  MEMORY_ALLOCATION_FAILED = -41,
+  IMPORT_PARAMETER_MISMATCH = -44,
+  SECURE_HW_ACCESS_DENIED = -45,
+  OPERATION_CANCELLED = -46,
+  CONCURRENT_ACCESS_CONFLICT = -47,
+  SECURE_HW_BUSY = -48,
+  SECURE_HW_COMMUNICATION_FAILED = -49,
+  UNSUPPORTED_EC_FIELD = -50,
+  MISSING_NONCE = -51,
+  INVALID_NONCE = -52,
+  MISSING_MAC_LENGTH = -53,
+  KEY_RATE_LIMIT_EXCEEDED = -54,
+  CALLER_NONCE_PROHIBITED = -55,
+  KEY_MAX_OPS_EXCEEDED = -56,
+  INVALID_MAC_LENGTH = -57,
+  MISSING_MIN_MAC_LENGTH = -58,
+  UNSUPPORTED_MIN_MAC_LENGTH = -59,
+  UNSUPPORTED_KDF = -60,
+  UNSUPPORTED_EC_CURVE = -61,
+  KEY_REQUIRES_UPGRADE = -62,
+  ATTESTATION_CHALLENGE_MISSING = -63,
+  KEYMINT_NOT_CONFIGURED = -64,
+  ATTESTATION_APPLICATION_ID_MISSING = -65,
+  CANNOT_ATTEST_IDS = -66,
+  ROLLBACK_RESISTANCE_UNAVAILABLE = -67,
+  HARDWARE_TYPE_UNAVAILABLE = -68,
+  PROOF_OF_PRESENCE_REQUIRED = -69,
+  CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+  NO_USER_CONFIRMATION = -71,
+  DEVICE_LOCKED = -72,
+  EARLY_BOOT_ENDED = -73,
+  ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+  ATTESTATION_IDS_NOT_PROVISIONED = -75,
+  INVALID_OPERATION = -76,
+  STORAGE_KEY_UNSUPPORTED = -77,
+  INCOMPATIBLE_MGF_DIGEST = -78,
+  UNSUPPORTED_MGF_DIGEST = -79,
+  MISSING_NOT_BEFORE = -80,
+  MISSING_NOT_AFTER = -81,
+  MISSING_ISSUER_SUBJECT = -82,
+  INVALID_ISSUER_SUBJECT = -83,
+  BOOT_LEVEL_EXCEEDED = -84,
+  HARDWARE_NOT_YET_AVAILABLE = -85,
+  UNIMPLEMENTED = -100,
+  VERSION_MISMATCH = -101,
+  UNKNOWN_ERROR = -1000,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
new file mode 100644
index 0000000..2e07924
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -0,0 +1,44 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable HardwareAuthToken {
+  long challenge;
+  long userId;
+  long authenticatorId;
+  android.hardware.security.keymint.HardwareAuthenticatorType authenticatorType = android.hardware.security.keymint.HardwareAuthenticatorType.NONE;
+  android.hardware.security.secureclock.Timestamp timestamp;
+  byte[] mac;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..dfc98f0
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -0,0 +1,42 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum HardwareAuthenticatorType {
+  NONE = 0,
+  PASSWORD = 1,
+  FINGERPRINT = 2,
+  ANY = -1,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
new file mode 100644
index 0000000..fa643fc
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@SensitiveData @VintfStability
+interface IKeyMintDevice {
+  android.hardware.security.keymint.KeyMintHardwareInfo getHardwareInfo();
+  void addRngEntropy(in byte[] data);
+  android.hardware.security.keymint.KeyCreationResult generateKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in @nullable android.hardware.security.keymint.AttestationKey attestationKey);
+  android.hardware.security.keymint.KeyCreationResult importKey(in android.hardware.security.keymint.KeyParameter[] keyParams, in android.hardware.security.keymint.KeyFormat keyFormat, in byte[] keyData, in @nullable android.hardware.security.keymint.AttestationKey attestationKey);
+  android.hardware.security.keymint.KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, in byte[] wrappingKeyBlob, in byte[] maskingKey, in android.hardware.security.keymint.KeyParameter[] unwrappingParams, in long passwordSid, in long biometricSid);
+  byte[] upgradeKey(in byte[] keyBlobToUpgrade, in android.hardware.security.keymint.KeyParameter[] upgradeParams);
+  void deleteKey(in byte[] keyBlob);
+  void deleteAllKeys();
+  void destroyAttestationIds();
+  android.hardware.security.keymint.BeginResult begin(in android.hardware.security.keymint.KeyPurpose purpose, in byte[] keyBlob, in android.hardware.security.keymint.KeyParameter[] params, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken);
+  void deviceLocked(in boolean passwordOnly, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken);
+  void earlyBootEnded();
+  byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
+  android.hardware.security.keymint.KeyCharacteristics[] getKeyCharacteristics(in byte[] keyBlob, in byte[] appId, in byte[] appData);
+  const int AUTH_TOKEN_MAC_LENGTH = 32;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
new file mode 100644
index 0000000..4ab4ffe
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@SensitiveData @VintfStability
+interface IKeyMintOperation {
+  void updateAad(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
+  byte[] update(in byte[] input, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timeStampToken);
+  byte[] finish(in @nullable byte[] input, in @nullable byte[] signature, in @nullable android.hardware.security.keymint.HardwareAuthToken authToken, in @nullable android.hardware.security.secureclock.TimeStampToken timestampToken, in @nullable byte[] confirmationToken);
+  void abort();
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
new file mode 100644
index 0000000..f566462
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+interface IRemotelyProvisionedComponent {
+  android.hardware.security.keymint.RpcHardwareInfo getHardwareInfo();
+  byte[] generateEcdsaP256KeyPair(in boolean testMode, out android.hardware.security.keymint.MacedPublicKey macedPublicKey);
+  byte[] generateCertificateRequest(in boolean testMode, in android.hardware.security.keymint.MacedPublicKey[] keysToSign, in byte[] endpointEncryptionCertChain, in byte[] challenge, out android.hardware.security.keymint.DeviceInfo deviceInfo, out android.hardware.security.keymint.ProtectedData protectedData);
+  const int STATUS_FAILED = 1;
+  const int STATUS_INVALID_MAC = 2;
+  const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
+  const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
+  const int STATUS_INVALID_EEK = 5;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
new file mode 100644
index 0000000..008381f
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable KeyCharacteristics {
+  android.hardware.security.keymint.SecurityLevel securityLevel = android.hardware.security.keymint.SecurityLevel.SOFTWARE;
+  android.hardware.security.keymint.KeyParameter[] authorizations;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl
new file mode 100644
index 0000000..9f77d3e
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable KeyCreationResult {
+  byte[] keyBlob;
+  android.hardware.security.keymint.KeyCharacteristics[] keyCharacteristics;
+  android.hardware.security.keymint.Certificate[] certificateChain;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
new file mode 100644
index 0000000..9560d8d
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyFormat.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum KeyFormat {
+  X509 = 0,
+  PKCS8 = 1,
+  RAW = 3,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
new file mode 100644
index 0000000..2113e42a
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable KeyMintHardwareInfo {
+  int versionNumber;
+  android.hardware.security.keymint.SecurityLevel securityLevel = android.hardware.security.keymint.SecurityLevel.SOFTWARE;
+  @utf8InCpp String keyMintName;
+  @utf8InCpp String keyMintAuthorName;
+  boolean timestampTokenRequired;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
new file mode 100644
index 0000000..4b3c659
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyOrigin.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum KeyOrigin {
+  GENERATED = 0,
+  DERIVED = 1,
+  IMPORTED = 2,
+  RESERVED = 3,
+  SECURELY_IMPORTED = 4,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
new file mode 100644
index 0000000..c5a1e01
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameter.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable KeyParameter {
+  android.hardware.security.keymint.Tag tag = android.hardware.security.keymint.Tag.INVALID;
+  android.hardware.security.keymint.KeyParameterValue value;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
new file mode 100644
index 0000000..7a0b074
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+union KeyParameterValue {
+  int invalid;
+  android.hardware.security.keymint.Algorithm algorithm;
+  android.hardware.security.keymint.BlockMode blockMode;
+  android.hardware.security.keymint.PaddingMode paddingMode;
+  android.hardware.security.keymint.Digest digest;
+  android.hardware.security.keymint.EcCurve ecCurve;
+  android.hardware.security.keymint.KeyOrigin origin;
+  android.hardware.security.keymint.KeyPurpose keyPurpose;
+  android.hardware.security.keymint.HardwareAuthenticatorType hardwareAuthenticatorType;
+  android.hardware.security.keymint.SecurityLevel securityLevel;
+  boolean boolValue;
+  int integer;
+  long longInteger;
+  long dateTime;
+  byte[] blob;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
new file mode 100644
index 0000000..b84bec1
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/KeyPurpose.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum KeyPurpose {
+  ENCRYPT = 0,
+  DECRYPT = 1,
+  SIGN = 2,
+  VERIFY = 3,
+  WRAP_KEY = 5,
+  AGREE_KEY = 6,
+  ATTEST_KEY = 7,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
new file mode 100644
index 0000000..8095e8c
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable MacedPublicKey {
+  byte[] macedKey;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
new file mode 100644
index 0000000..dba4a8a
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/PaddingMode.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum PaddingMode {
+  NONE = 1,
+  RSA_OAEP = 2,
+  RSA_PSS = 3,
+  RSA_PKCS1_1_5_ENCRYPT = 4,
+  RSA_PKCS1_1_5_SIGN = 5,
+  PKCS7 = 64,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
new file mode 100644
index 0000000..d1610b4
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/ProtectedData.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@VintfStability
+parcelable ProtectedData {
+  byte[] protectedData;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
new file mode 100644
index 0000000..06bce19
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable RpcHardwareInfo {
+  int versionNumber;
+  @utf8InCpp String rpcAuthorName;
+  int supportedEekCurve = 0;
+  const int CURVE_NONE = 0;
+  const int CURVE_P256 = 1;
+  const int CURVE_25519 = 2;
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
new file mode 100644
index 0000000..0d278e0
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/SecurityLevel.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum SecurityLevel {
+  SOFTWARE = 0,
+  TRUSTED_ENVIRONMENT = 1,
+  STRONGBOX = 2,
+  KEYSTORE = 100,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
new file mode 100644
index 0000000..e310b44
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/Tag.aidl
@@ -0,0 +1,103 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum Tag {
+  INVALID = 0,
+  PURPOSE = 536870913,
+  ALGORITHM = 268435458,
+  KEY_SIZE = 805306371,
+  BLOCK_MODE = 536870916,
+  DIGEST = 536870917,
+  PADDING = 536870918,
+  CALLER_NONCE = 1879048199,
+  MIN_MAC_LENGTH = 805306376,
+  EC_CURVE = 268435466,
+  RSA_PUBLIC_EXPONENT = 1342177480,
+  INCLUDE_UNIQUE_ID = 1879048394,
+  RSA_OAEP_MGF_DIGEST = 536871115,
+  BOOTLOADER_ONLY = 1879048494,
+  ROLLBACK_RESISTANCE = 1879048495,
+  HARDWARE_TYPE = 268435760,
+  EARLY_BOOT_ONLY = 1879048497,
+  ACTIVE_DATETIME = 1610613136,
+  ORIGINATION_EXPIRE_DATETIME = 1610613137,
+  USAGE_EXPIRE_DATETIME = 1610613138,
+  MIN_SECONDS_BETWEEN_OPS = 805306771,
+  MAX_USES_PER_BOOT = 805306772,
+  USAGE_COUNT_LIMIT = 805306773,
+  USER_ID = 805306869,
+  USER_SECURE_ID = -1610612234,
+  NO_AUTH_REQUIRED = 1879048695,
+  USER_AUTH_TYPE = 268435960,
+  AUTH_TIMEOUT = 805306873,
+  ALLOW_WHILE_ON_BODY = 1879048698,
+  TRUSTED_USER_PRESENCE_REQUIRED = 1879048699,
+  TRUSTED_CONFIRMATION_REQUIRED = 1879048700,
+  UNLOCKED_DEVICE_REQUIRED = 1879048701,
+  APPLICATION_ID = -1879047591,
+  APPLICATION_DATA = -1879047492,
+  CREATION_DATETIME = 1610613437,
+  ORIGIN = 268436158,
+  ROOT_OF_TRUST = -1879047488,
+  OS_VERSION = 805307073,
+  OS_PATCHLEVEL = 805307074,
+  UNIQUE_ID = -1879047485,
+  ATTESTATION_CHALLENGE = -1879047484,
+  ATTESTATION_APPLICATION_ID = -1879047483,
+  ATTESTATION_ID_BRAND = -1879047482,
+  ATTESTATION_ID_DEVICE = -1879047481,
+  ATTESTATION_ID_PRODUCT = -1879047480,
+  ATTESTATION_ID_SERIAL = -1879047479,
+  ATTESTATION_ID_IMEI = -1879047478,
+  ATTESTATION_ID_MEID = -1879047477,
+  ATTESTATION_ID_MANUFACTURER = -1879047476,
+  ATTESTATION_ID_MODEL = -1879047475,
+  VENDOR_PATCHLEVEL = 805307086,
+  BOOT_PATCHLEVEL = 805307087,
+  DEVICE_UNIQUE_ATTESTATION = 1879048912,
+  IDENTITY_CREDENTIAL_KEY = 1879048913,
+  STORAGE_KEY = 1879048914,
+  ASSOCIATED_DATA = -1879047192,
+  NONCE = -1879047191,
+  MAC_LENGTH = 805307371,
+  RESET_SINCE_ID_ROTATION = 1879049196,
+  CONFIRMATION_TOKEN = -1879047187,
+  CERTIFICATE_SERIAL = -2147482642,
+  CERTIFICATE_SUBJECT = -1879047185,
+  CERTIFICATE_NOT_BEFORE = 1610613744,
+  CERTIFICATE_NOT_AFTER = 1610613745,
+  MAX_BOOT_LEVEL = 805307378,
+}
diff --git a/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
new file mode 100644
index 0000000..a7d1de5
--- /dev/null
+++ b/security/keymint/aidl/aidl_api/android.hardware.security.keymint/current/android/hardware/security/keymint/TagType.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.keymint;
+/* @hide */
+@Backing(type="int") @VintfStability
+enum TagType {
+  INVALID = 0,
+  ENUM = 268435456,
+  ENUM_REP = 536870912,
+  UINT = 805306368,
+  UINT_REP = 1073741824,
+  ULONG = 1342177280,
+  DATE = 1610612736,
+  BOOL = 1879048192,
+  BIGNUM = -2147483648,
+  BYTES = -1879048192,
+  ULONG_REP = -1610612736,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
new file mode 100644
index 0000000..1820893
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Algorithm.aidl
@@ -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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Algorithms provided by IKeyMintDevice implementations.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum Algorithm {
+    /** Asymmetric algorithms. */
+    RSA = 1,
+    /** 2 removed, do not reuse. */
+    EC = 3,
+
+    /** Block cipher algorithms */
+    AES = 32,
+    TRIPLE_DES = 33,
+
+    /** MAC algorithms */
+    HMAC = 128,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
new file mode 100644
index 0000000..4e3008f
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/AttestationKey.aidl
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * Contains a key blob with Tag::ATTEST_KEY that can be used to sign an attestation certificate,
+ * and the DER-encoded X.501 Subject Name that will be placed in the Issuer field of the attestation
+ * certificate.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable AttestationKey {
+    /**
+     * Key blob containing a key pair with KeyPurpose::ATTEST_KEY
+     */
+    byte[] keyBlob;
+
+    /**
+     * Key parameters needed to use the key in keyBlob, notably Tag::APPLICATION_ID and
+     * Tag::APPLICATION_DATA, if they were provided during generation of the key in keyBlob.
+     */
+    KeyParameter[] attestKeyParams;
+
+    /**
+     * The issuerSubjectName to use in the generated attestation.
+     */
+    byte[] issuerSubjectName;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
new file mode 100644
index 0000000..b5336b9
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/BeginResult.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyParameter;
+
+/**
+ * This is all the results returned by the IKeyMintDevice begin() function.
+ * @hide
+ */
+@VintfStability
+parcelable BeginResult {
+    /**
+     * This is the challenge used to verify authorization of an operation.
+     * See IKeyMintOperation.aidl entrypoints updateAad() and update().
+     */
+    long challenge;
+
+    /**
+     * begin() uses this field to return additional data from the operation
+     * initialization, notably to return the IV or nonce from operations
+     * that generate an IV or nonce.
+     */
+    KeyParameter[] params;
+    IKeyMintOperation operation;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
new file mode 100644
index 0000000..749da81
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/BlockMode.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Symmetric block cipher modes provided by IKeyMintDevice implementations.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum BlockMode {
+    /*
+     * Unauthenticated modes, usable only for encryption/decryption and not generally recommended
+     * except for compatibility with existing other protocols.
+     */
+    ECB = 1,
+    CBC = 2,
+    CTR = 3,
+
+    /*
+     * Authenticated modes, usable for encryption/decryption and signing/verification.  Recommended
+     * over unauthenticated modes for all purposes.
+     */
+    GCM = 32,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
new file mode 100644
index 0000000..21dfdd5
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Certificate.aidl
@@ -0,0 +1,29 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * This encodes an IKeyMintDevice certificate, generated for a KeyMint asymmetric public key.
+ * @hide
+ */
+@VintfStability
+parcelable Certificate {
+    /**
+     * EncodedCertificate contains the bytes of a DER-encoded X.509 certificate.
+     */
+    byte[] encodedCertificate;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
new file mode 100644
index 0000000..b0761bf
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/DeviceInfo.aidl
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * DeviceInfo contains information about the device that's fed in as AAD in the signature of the
+ * device private key over the MAC key used for the bundle of public keys. These values are intended
+ * to be checked by the server to verify that the certificate signing request crafted by
+ * an IRemotelyProvisionedComponent HAL instance is coming from the expected device based
+ * on values initially uploaded during device manufacture in the factory.
+ * @hide
+ */
+@VintfStability
+parcelable DeviceInfo {
+    /**
+     * DeviceInfo is a CBOR Map structure described by the following CDDL.
+     *
+     *     DeviceInfo = {
+     *         ? "brand" : tstr,
+     *         ? "manufacturer" : tstr,
+     *         ? "product" : tstr,
+     *         ? "model" : tstr,
+     *         ? "board" : tstr,
+     *         ? "vb_state" : "green" / "yellow" / "orange",    // Taken from the AVB values
+     *         ? "bootloader_state" : "locked" / "unlocked",    // Taken from the AVB values
+     *         ? "vbmeta_digest": bstr,                         // Taken from the AVB values
+     *         ? "os_version" : tstr,                    // Same as android.os.Build.VERSION.release
+     *         ? "system_patch_level" : uint,                   // YYYYMMDD
+     *         ? "boot_patch_level" : uint,                     // YYYYMMDD
+     *         ? "vendor_patch_level" : uint,                   // YYYYMMDD
+     *         "version" : 1,                      // The CDDL schema version.
+     *         "security_level" : "tee" / "strongbox"
+     *         "att_id_state": "locked" / "open",  // Attestation IDs State. If "locked", this
+     *                                             // indicates a device's attestable IDs are
+     *                                             // factory-locked and immutable. If "open",
+     *                                             // this indicates the device is still in a
+     *                                             // provisionable state and the attestable IDs
+     *                                             // are not yet frozen.
+     *     }
+     */
+    byte[] deviceInfo;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
new file mode 100644
index 0000000..a8768c3
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Digest.aidl
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Digests provided by keyMint implementations.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum Digest {
+    NONE = 0,
+    MD5 = 1,
+    SHA1 = 2,
+    SHA_2_224 = 3,
+    SHA_2_256 = 4,
+    SHA_2_384 = 5,
+    SHA_2_512 = 6,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
new file mode 100644
index 0000000..5b1c10c
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/EcCurve.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Supported EC curves, used in ECDSA
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum EcCurve {
+    P_224 = 0,
+    P_256 = 1,
+    P_384 = 2,
+    P_521 = 3,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
new file mode 100644
index 0000000..137e6b6
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ErrorCode.aidl
@@ -0,0 +1,118 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * KeyMint error codes.  Aidl will return these error codes as service specific
+ * errors in EX_SERVICE_SPECIFIC.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum ErrorCode {
+    OK = 0,
+    ROOT_OF_TRUST_ALREADY_SET = -1,
+    UNSUPPORTED_PURPOSE = -2,
+    INCOMPATIBLE_PURPOSE = -3,
+    UNSUPPORTED_ALGORITHM = -4,
+    INCOMPATIBLE_ALGORITHM = -5,
+    UNSUPPORTED_KEY_SIZE = -6,
+    UNSUPPORTED_BLOCK_MODE = -7,
+    INCOMPATIBLE_BLOCK_MODE = -8,
+    UNSUPPORTED_MAC_LENGTH = -9,
+    UNSUPPORTED_PADDING_MODE = -10,
+    INCOMPATIBLE_PADDING_MODE = -11,
+    UNSUPPORTED_DIGEST = -12,
+    INCOMPATIBLE_DIGEST = -13,
+    INVALID_EXPIRATION_TIME = -14,
+    INVALID_USER_ID = -15,
+    INVALID_AUTHORIZATION_TIMEOUT = -16,
+    UNSUPPORTED_KEY_FORMAT = -17,
+    INCOMPATIBLE_KEY_FORMAT = -18,
+    UNSUPPORTED_KEY_ENCRYPTION_ALGORITHM = -19, /** For PKCS8 & PKCS12 */
+    UNSUPPORTED_KEY_VERIFICATION_ALGORITHM = -20, /** For PKCS8 & PKCS12 */
+    INVALID_INPUT_LENGTH = -21,
+    KEY_EXPORT_OPTIONS_INVALID = -22,
+    DELEGATION_NOT_ALLOWED = -23,
+    KEY_NOT_YET_VALID = -24,
+    KEY_EXPIRED = -25,
+    KEY_USER_NOT_AUTHENTICATED = -26,
+    OUTPUT_PARAMETER_NULL = -27,
+    INVALID_OPERATION_HANDLE = -28,
+    INSUFFICIENT_BUFFER_SPACE = -29,
+    VERIFICATION_FAILED = -30,
+    TOO_MANY_OPERATIONS = -31,
+    UNEXPECTED_NULL_POINTER = -32,
+    INVALID_KEY_BLOB = -33,
+    IMPORTED_KEY_NOT_ENCRYPTED = -34,
+    IMPORTED_KEY_DECRYPTION_FAILED = -35,
+    IMPORTED_KEY_NOT_SIGNED = -36,
+    IMPORTED_KEY_VERIFICATION_FAILED = -37,
+    INVALID_ARGUMENT = -38,
+    UNSUPPORTED_TAG = -39,
+    INVALID_TAG = -40,
+    MEMORY_ALLOCATION_FAILED = -41,
+    IMPORT_PARAMETER_MISMATCH = -44,
+    SECURE_HW_ACCESS_DENIED = -45,
+    OPERATION_CANCELLED = -46,
+    CONCURRENT_ACCESS_CONFLICT = -47,
+    SECURE_HW_BUSY = -48,
+    SECURE_HW_COMMUNICATION_FAILED = -49,
+    UNSUPPORTED_EC_FIELD = -50,
+    MISSING_NONCE = -51,
+    INVALID_NONCE = -52,
+    MISSING_MAC_LENGTH = -53,
+    KEY_RATE_LIMIT_EXCEEDED = -54,
+    CALLER_NONCE_PROHIBITED = -55,
+    KEY_MAX_OPS_EXCEEDED = -56,
+    INVALID_MAC_LENGTH = -57,
+    MISSING_MIN_MAC_LENGTH = -58,
+    UNSUPPORTED_MIN_MAC_LENGTH = -59,
+    UNSUPPORTED_KDF = -60,
+    UNSUPPORTED_EC_CURVE = -61,
+    KEY_REQUIRES_UPGRADE = -62,
+    ATTESTATION_CHALLENGE_MISSING = -63,
+    KEYMINT_NOT_CONFIGURED = -64,
+    ATTESTATION_APPLICATION_ID_MISSING = -65,
+    CANNOT_ATTEST_IDS = -66,
+    ROLLBACK_RESISTANCE_UNAVAILABLE = -67,
+    HARDWARE_TYPE_UNAVAILABLE = -68,
+    PROOF_OF_PRESENCE_REQUIRED = -69,
+    CONCURRENT_PROOF_OF_PRESENCE_REQUESTED = -70,
+    NO_USER_CONFIRMATION = -71,
+    DEVICE_LOCKED = -72,
+    EARLY_BOOT_ENDED = -73,
+    ATTESTATION_KEYS_NOT_PROVISIONED = -74,
+    ATTESTATION_IDS_NOT_PROVISIONED = -75,
+    INVALID_OPERATION = -76,
+    STORAGE_KEY_UNSUPPORTED = -77,
+    INCOMPATIBLE_MGF_DIGEST = -78,
+    UNSUPPORTED_MGF_DIGEST = -79,
+    MISSING_NOT_BEFORE = -80,
+    MISSING_NOT_AFTER = -81,
+    MISSING_ISSUER_SUBJECT = -82,
+    INVALID_ISSUER_SUBJECT = -83,
+    BOOT_LEVEL_EXCEEDED = -84,
+    HARDWARE_NOT_YET_AVAILABLE = -85,
+
+    UNIMPLEMENTED = -100,
+    VERSION_MISMATCH = -101,
+
+    UNKNOWN_ERROR = -1000,
+
+    // Implementer's namespace for error codes starts at -10000.
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
new file mode 100644
index 0000000..0933bd5
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthToken.aidl
@@ -0,0 +1,85 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.secureclock.Timestamp;
+
+/**
+ * HardwareAuthToken is used to prove successful user authentication, to unlock the use of a key.
+ *
+ * HardwareAuthTokens are produced by other secure environment applications, notably GateKeeper and
+ * biometric authenticators, in response to successful user authentication events.  These tokens are
+ * passed to begin(), update(), and finish() to prove that authentication occurred.  See those
+ * methods for more details.  It is up to the caller to determine which of the generated auth tokens
+ * is appropriate for a given key operation.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable HardwareAuthToken {
+    /**
+     * challenge is a value that's used to enable authentication tokens to authorize specific
+     * events.  The primary use case for challenge is to authorize an IKeyMintDevice cryptographic
+     * operation, for keys that require authentication per operation. See begin() for details.
+     */
+    long challenge;
+
+    /**
+     *  userId is the a "secure" user ID.  It is not related to any Android user ID or UID, but is
+     *  created in the Gatekeeper application in the secure environment.
+     */
+    long userId;
+
+    /**
+     *  authenticatorId is the a "secure" user ID.  It is not related to any Android user ID or UID,
+     *  but is created in an authentication application in the secure environment, such as the
+     *  Fingerprint application.
+     */
+    long authenticatorId;
+
+    /**
+     * authenticatorType describes the type of authentication that took place, e.g. password or
+     * fingerprint.
+     */
+    HardwareAuthenticatorType authenticatorType = HardwareAuthenticatorType.NONE;
+
+    /**
+     * timestamp indicates when the user authentication took place, in milliseconds since some
+     * starting point (generally the most recent device boot) which all of the applications within
+     * one secure environment must agree upon.  This timestamp is used to determine whether or not
+     * the authentication occurred recently enough to unlock a key (see Tag::AUTH_TIMEOUT).
+     */
+    Timestamp timestamp;
+
+    /**
+     * MACs are computed with a backward-compatible method, used by Keymaster 3.0, Gatekeeper 1.0
+     * and Fingerprint 1.0, as well as pre-treble HALs.
+     *
+     * The MAC is Constants::AUTH_TOKEN_MAC_LENGTH bytes in length and is computed as follows:
+     *
+     *     HMAC_SHA256(
+     *         H, 0 || challenge || user_id || authenticator_id || authenticator_type || timestamp)
+     *
+     * where ``||'' represents concatenation, the leading zero is a single byte, and all integers
+     * are represented as unsigned values, the full width of the type.  The challenge, userId and
+     * authenticatorId values are in machine order, but authenticatorType and timestamp are in
+     * network order (big-endian).  This odd construction is compatible with the hw_auth_token_t
+     * structure.
+     */
+    byte[] mac;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..2d9d0ff
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/HardwareAuthenticatorType.aidl
@@ -0,0 +1,33 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Hardware authentication type, used by HardwareAuthTokens to specify the mechanism used to
+ * authentiate the user, and in KeyCharacteristics to specify the allowable mechanisms for
+ * authenticating to activate a key.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+    NONE = 0,
+    PASSWORD = 1 << 0,
+    FINGERPRINT = 1 << 1,
+    // Additional entries must be powers of 2.
+    ANY = 0xFFFFFFFF,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
new file mode 100644
index 0000000..1849723
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintDevice.aidl
@@ -0,0 +1,835 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.AttestationKey;
+import android.hardware.security.keymint.BeginResult;
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.IKeyMintOperation;
+import android.hardware.security.keymint.KeyCharacteristics;
+import android.hardware.security.keymint.KeyCreationResult;
+import android.hardware.security.keymint.KeyFormat;
+import android.hardware.security.keymint.KeyMintHardwareInfo;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.SecurityLevel;
+import android.hardware.security.secureclock.TimeStampToken;
+
+/**
+ * KeyMint device definition.
+ *
+ * == Features ==
+ *
+ * An IKeyMintDevice provides cryptographic services, including the following categories of
+ * operations:
+ *
+ * o   Key generation
+ * o   Import of asymmetric keys
+ * o   Import of raw symmetric keys
+ * o   Asymmetric decryption with appropriate padding modes
+ * o   Asymmetric signing with digesting and appropriate padding modes
+ * o   Symmetric encryption and decryption in appropriate modes, including an AEAD mode
+ * o   Generation and verification of symmetric message authentication codes
+ * o   Attestation to the presence and configuration of asymmetric keys.
+ *
+ * Protocol elements, such as purpose, mode and padding, as well as access control constraints, must
+ * be specified by the caller when keys are generated or imported and must be permanently bound to
+ * the key, ensuring that the key cannot be used in any other way.
+ *
+ * In addition to the list above, IKeyMintDevice implementations must provide one more service
+ * which is not exposed as an API but used internally: Random number generation.  The random number
+ * generator must be high-quality and must be used for generation of keys, initialization vectors,
+ * random padding and other elements of secure protocols that require randomness.
+ *
+ * == Types of IKeyMintDevices ==
+ *
+ * All of the operations and storage of key material must occur in a secure environment.  Secure
+ * environments may be either:
+ *
+ * 1.  Isolated execution environments, such as a separate virtual machine, hypervisor or
+ *      purpose-built trusted execution environment like ARM TrustZone.  The isolated environment
+ *      must provide complete separation from the Android kernel and user space (collectively called
+ *      the "non-secure world", or NSW) so that nothing running in the NSW can observe or manipulate
+ *      the results of any computation in the isolated environment.  Isolated execution environments
+ *      are identified by the SecurityLevel TRUSTED_ENVIRONMENT.
+ *
+ * 2.  Completely separate, purpose-built and certified secure CPUs, called "StrongBox" devices.
+ *      Examples of StrongBox devices are embedded Secure Elements (eSE) or on-SoC secure processing
+ *      units (iSE).  StrongBox environments are identified by the SecurityLevel STRONGBOX.  To
+ *      qualify as a StrongBox, a device must meet the requirements specified in CDD 9.11.2.
+ *
+ * == Necessary Primitives ==
+ *
+ * All IKeyMintDevice implementations must provide support for the following:
+ *
+ * o   RSA
+ *
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support 2048, 3072 and 4096-bit keys.
+ *        STRONGBOX IKeyMintDevices must support 2048-bit keys.
+ *      - Public exponent F4 (2^16+1)
+ *      - Unpadded, RSASSA-PSS and RSASSA-PKCS1-v1_5 padding modes for RSA signing
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support MD5, SHA1, SHA-2 224, SHA-2 256, SHA-2
+ *        384 and SHA-2 512 digest modes for RSA signing.  STRONGBOX IKeyMintDevices must support
+ *        SHA-2 256.
+ *      - Unpadded, RSAES-OAEP and RSAES-PKCS1-v1_5 padding modes for RSA encryption.
+ *
+ * o   ECDSA
+ *
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support NIST curves P-224, P-256, P-384 and
+ *        P-521.  STRONGBOX IKeyMintDevices must support NIST curve P-256.
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support SHA1, SHA-2 224, SHA-2 256, SHA-2
+ *        384 and SHA-2 512 digest modes.  STRONGBOX IKeyMintDevices must support SHA-2 256.
+ *
+ * o   AES
+ *
+ *      - 128 and 256-bit keys
+ *      - CBC, CTR, ECB and GCM modes.  The GCM mode must not allow the use of tags smaller than 96
+ *        bits or nonce lengths other than 96 bits.
+ *      - CBC and ECB modes must support unpadded and PKCS7 padding modes.  With no padding CBC and
+ *        ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
+ *        multiple of the AES block size.  With PKCS7 padding, GCM and CTR operations must fail with
+ *        ErrorCode::INCOMPATIBLE_PADDING_MODE.
+ *
+ * o   3DES
+ *
+ *      - 168-bit keys.
+ *      - CBC and ECB mode.
+ *      - CBC and ECB modes must support unpadded and PKCS7 padding modes.  With no padding CBC and
+ *        ECB-mode operations must fail with ErrorCode::INVALID_INPUT_LENGTH if the input isn't a
+ *        multiple of the DES block size.
+ *
+ * o   HMAC
+ *
+ *      - Any key size that is between 64 and 512 bits (inclusive) and a multiple of 8 must be
+ *        supported.  STRONGBOX IKeyMintDevices must not support keys larger than 512 bits.
+ *      - TRUSTED_ENVIRONMENT IKeyMintDevices must support MD-5, SHA1, SHA-2-224, SHA-2-256,
+ *        SHA-2-384 and SHA-2-512.  STRONGBOX IKeyMintDevices must support SHA-2-256.
+ *
+ * == Key Access Control ==
+ *
+ * Hardware-based keys that can never be extracted from the device don't provide much security if an
+ * attacker can use them at will (though they're more secure than keys which can be
+ * exfiltrated).  Therefore, IKeyMintDevice must enforce access controls.
+ *
+ * Access controls are defined as "authorization lists" of tag/value pairs.  Authorization tags are
+ * 32-bit integers from the Tag enum, and the values are a variety of types, defined in the TagType
+ * enum.  Some tags may be repeated to specify multiple values.  Whether a tag may be repeated is
+ * specified in the documentation for the tag and in the TagType.  When a key is created or
+ * imported, the caller specifies a `key_description` authorization list.  The IKeyMintDevice must
+ * determine which tags it can and cannot enforce, and at what SecurityLevel, and return an array of
+ * `KeyCharacteristics` structures that contains everything it will enforce, associated with the
+ * appropriate security level, which is one of SOFTWARE, TRUSTED_ENVIRONMENT and STRONGBOX.
+ * Typically, implementations will only return a single KeyCharacteristics structure, because
+ * everything they enforce is enforced at the same security level.  There may be cases, however, for
+ * which multiple security levels are relevant. One example is that of a StrongBox IKeyMintDevice
+ * that relies on a TEE to enforce biometric user authentication.  In that case, the generate/import
+ * methods must return two KeyCharacteristics structs, one with SecurityLevel::TRUSTED_ENVIRONMENT
+ * and the biometric authentication-related tags, and another with SecurityLevel::STRONGBOX and
+ * everything else.  The IKeyMintDevice must also add the following authorizations to the
+ * appropriate list:
+ *
+ * o    Tag::OS_VERSION
+ * o    Tag::OS_PATCHLEVEL
+ * o    Tag::VENDOR_PATCHLEVEL
+ * o    Tag::BOOT_PATCHLEVEL
+ * o    Tag::ORIGIN
+ *
+ * The IKeyMintDevice must ignore unknown tags.
+ *
+ * The caller may provide the current date time in the keyParameter CREATION_DATETIME tag, but
+ * this is optional and informational only.
+ *
+ * All authorization tags and their values enforced by an IKeyMintDevice must be cryptographically
+ * bound to the private/secret key material such that any modification of the portion of the key
+ * blob that contains the authorization list makes it impossible for the secure environment to
+ * obtain the private/secret key material.  The recommended approach to meet this requirement is to
+ * use the full set of authorization tags associated with a key as input to a secure key derivation
+ * function used to derive a key (the KEK) that is used to encrypt the private/secret key material.
+ * Note that it is NOT acceptable to use a static KEK to encrypt the private/secret key material
+ * with an AEAD cipher mode, using the enforced authorization tags as AAD.  This is because
+ * Tag::APPLICATION_DATA must not be included in the authorization tags stored in the key blob, but
+ * must be provided by the caller for every use.  Assuming the Tag::APPLICATION_DATA value has
+ * sufficient entropy, this provides a cryptographic guarantee that an attacker cannot use a key
+ * without knowing the Tag::APPLICATION_DATA value, even if they compromise the IKeyMintDevice.
+ *
+ * IKeyMintDevice implementations must ignore any tags they cannot enforce and must not return them
+ * in KeyCharacteristics.  For example, Tag::ORIGINATION_EXPIRE_DATETIME provides the date and time
+ * after which a key may not be used to encrypt or sign new messages.  Unless the IKeyMintDevice has
+ * access to a secure source of current date/time information, it is not possible for the
+ * IKeyMintDevice to enforce this tag.  An IKeyMintDevice implementation will not rely on the
+ * non-secure world's notion of time, because it could be controlled by an attacker. Similarly, it
+ * cannot rely on GPSr time, even if it has exclusive control of the GPSr, because that might be
+ * spoofed by attacker RF signals.
+ *
+ * Some tags must be enforced by the IKeyMintDevice.  See the detailed documentation on each Tag
+ * in Tag.aidl.
+ *
+ * == Root of Trust Binding ==
+ *
+ * IKeyMintDevice keys must be bound to a root of trust, which is a bitstring that must be
+ * provided to the secure environment (by an unspecified, implementation-defined mechanism) during
+ * startup, preferably by the bootloader.  This bitstring must be cryptographically bound to every
+ * key managed by the IKeyMintDevice.  As above, the recommended mechanism for this cryptographic
+ * binding is to include the Root of Trust data in the input to the key derivation function used to
+ * derive a key that is used to encrypt the private/secret key material.
+ *
+ * The root of trust consists of a bitstring that must be derived from the public key used by
+ * Verified Boot to verify the signature on the boot image and from the lock state of the
+ * device.  If the public key is changed to allow a different system image to be used or if the
+ * lock state is changed, then all of the IKeyMintDevice-protected keys created by the previous
+ * system state must be unusable, unless the previous state is restored.  The goal is to increase
+ * the value of the software-enforced key access controls by making it impossible for an attacker-
+ * installed operating system to use IKeyMintDevice keys.
+ *
+ * == Version Binding ==
+ *
+ * All keys must also be bound to the operating system and patch level of the system image and the
+ * patch levels of the vendor image and boot image.  This ensures that an attacker who discovers a
+ * weakness in an old version of the software cannot roll a device back to the vulnerable version
+ * and use keys created with the newer version.  In addition, when a key with a given version and
+ * patch level is used on a device that has been upgraded to a newer version or patch level, the
+ * key must be upgraded (See IKeyMintDevice::upgradeKey()) before it can be used, and the previous
+ * version of the key must be invalidated.  In this way, as the device is upgraded, the keys will
+ * "ratchet" forward along with the device, but any reversion of the device to a previous release
+ * will cause the keys to be unusable.
+ *
+ * This version information must be associated with every key as a set of tag/value pairs in the
+ * hardwareEnforced authorization list.  Tag::OS_VERSION, Tag::OS_PATCHLEVEL,
+ * Tag::VENDOR_PATCHLEVEL, and Tag::BOOT_PATCHLEVEL must be cryptographically bound to every
+ * IKeyMintDevice key, as described in the Key Access Control section above.
+ * @hide
+ */
+@VintfStability
+@SensitiveData
+interface IKeyMintDevice {
+    const int AUTH_TOKEN_MAC_LENGTH = 32;
+
+    /**
+     * @return info which contains information about the underlying IKeyMintDevice hardware, such
+     *         as version number, security level, keyMint name and author name.
+     */
+    KeyMintHardwareInfo getHardwareInfo();
+
+    /**
+     * Adds entropy to the RNG used by KeyMint.  Entropy added through this method must not be the
+     * only source of entropy used, and a secure mixing function must be used to mix the entropy
+     * provided by this method with internally-generated entropy.  The mixing function must be
+     * secure in the sense that if any one of the mixing function inputs is provided with any data
+     * the attacker cannot predict (or control), then the output of the seeded CRNG is
+     * indistinguishable from random.  Thus, if the entropy from any source is good, the output
+     * must be good.
+     *
+     * @param data Bytes to be mixed into the CRNG seed.  The caller must not provide more than 2
+     *        KiB of data per invocation.
+     *
+     * @return error ErrorCode::OK on success; ErrorCode::INVALID_INPUT_LENGTH if the caller
+     *         provides more than 2 KiB of data.
+     */
+    void addRngEntropy(in byte[] data);
+
+    /**
+     * Generates a new cryptographic key, specifying associated parameters, which must be
+     * cryptographically bound to the key.  IKeyMintDevice implementations must disallow any use
+     * of a key in any way inconsistent with the authorizations specified at generation time.  With
+     * respect to parameters that the secure environment cannot enforce, the secure environment's
+     * obligation is limited to ensuring that the unenforceable parameters associated with the key
+     * cannot be modified.  In addition, the characteristics returned by generateKey places
+     * parameters correctly in the tee-enforced and strongbox-enforced lists.
+     *
+     * In addition to the parameters provided, generateKey must add the following to the returned
+     * characteristics.
+     *
+     * o Tag::ORIGIN with the value KeyOrigin::GENERATED.
+     *
+     * o Tag::OS_VERSION, Tag::OS_PATCHLEVEL, Tag::VENDOR_PATCHLEVEL and Tag::BOOT_PATCHLEVEL with
+     *   appropriate values.
+     *
+     * The parameters provided to generateKey depend on the type of key being generated.  This
+     * section summarizes the necessary and optional tags for each type of key.  Tag::ALGORITHM is
+     * always necessary, to specify the type.
+     *
+     * == RSA Keys ==
+     *
+     * The following parameters are required to generate an RSA key:
+     *
+     * o Tag::KEY_SIZE specifies the size of the public modulus, in bits.  If omitted, generateKey
+     *   must return ErrorCode::UNSUPPORTED_KEY_SIZE.  Required values for TEE IKeyMintDevice
+     *   implementations are 1024, 2048, 3072 and 4096.  StrongBox IKeyMintDevice implementations
+     *   must support 2048.
+     *
+     * o Tag::RSA_PUBLIC_EXPONENT specifies the RSA public exponent value.  If omitted, generateKey
+     *   must return ErrorCode::INVALID_ARGUMENT.  The values 3 and 65537 must be supported.  It is
+     *   recommended to support all prime values up to 2^64.
+     *
+     * o Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER specify the valid date range for
+     *   the returned X.509 certificate holding the public key. If omitted, generateKey must return
+     *   ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
+     *
+     * The following parameters are not necessary to generate a usable RSA key, but generateKey must
+     * not return an error if they are omitted:
+     *
+     * o Tag::PURPOSE specifies allowed purposes.  All KeyPurpose values (see KeyPurpose.aidl)
+     *   except AGREE_KEY must be supported for RSA keys.
+     *
+     * o Tag::DIGEST specifies digest algorithms that may be used with the new key.  TEE
+     *   IKeyMintDevice implementations must support all Digest values (see digest.aidl) for RSA
+     *   keys.  StrongBox IKeyMintDevice implementations must support SHA_2_256.
+     *
+     * o Tag::PADDING specifies the padding modes that may be used with the new
+     *   key.  IKeyMintDevice implementations must support PaddingMode::NONE,
+     *   PaddingMode::RSA_OAEP, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_ENCRYPT and
+     *   PaddingMode::RSA_PKCS1_1_5_SIGN for RSA keys.
+     *
+     * == ECDSA Keys ==
+     *
+     * Tag::EC_CURVE must be provided to generate an ECDSA key.  If it is not provided, generateKey
+     * must return ErrorCode::UNSUPPORTED_KEY_SIZE. TEE IKeyMintDevice implementations must support
+     * all curves.  StrongBox implementations must support P_256.
+
+     * Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER must be provided to specify the
+     * valid date range for the returned X.509 certificate holding the public key. If omitted,
+     * generateKey must return ErrorCode::MISSING_NOT_BEFORE or ErrorCode::MISSING_NOT_AFTER.
+     *
+     * == AES Keys ==
+     *
+     * Only Tag::KEY_SIZE is required to generate an AES key.  If omitted, generateKey must return
+     * ErrorCode::UNSUPPORTED_KEY_SIZE.  128 and 256-bit key sizes must be supported.
+     *
+     * If Tag::BLOCK_MODE is specified with value BlockMode::GCM, then the caller must also provide
+     * Tag::MIN_MAC_LENGTH.  If omitted, generateKey must return ErrorCode::MISSING_MIN_MAC_LENGTH.
+     *
+     * == 3DES Keys ==
+     *
+     * Only Tag::KEY_SIZE is required to generate an 3DES key, and its value must be 168.  If
+     * omitted, generateKey must return ErrorCode::UNSUPPORTED_KEY_SIZE.
+     *
+     * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     *        provided in params.  See above for detailed specifications of which tags are required
+     *        for which types of keys.
+     *
+     * @param attestationKey, if provided, specifies the key that must be used to sign the
+     *        attestation certificate.  If `keyParams` does not contain a Tag::ATTESTATION_CHALLENGE
+     *        but `attestationKey` is non-null, the IKeyMintDevice must return
+     *        ErrorCode::ATTESTATION_CHALLENGE_MISSING. If the provided AttestationKey does not
+     *        contain a key blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the
+     *        IKeyMintDevice must return ErrorCode::INCOMPATIBLE_PURPOSE.  If the provided
+     *        AttestationKey has an empty issuer subject name, the IKeyMintDevice must return
+     *        ErrorCode::INVALID_ARGUMENT.
+     *
+     *        If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+     *        the KeyMint implementation does not have factory-provisioned attestation keys, it must
+     *        return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+     *
+     * @return The result of key creation.  See KeyCreationResult.aidl.
+     */
+    KeyCreationResult generateKey(
+            in KeyParameter[] keyParams, in @nullable AttestationKey attestationKey);
+
+    /**
+     * Imports key material into an IKeyMintDevice.  Key definition parameters and return values
+     * are the same as for generateKey, with the following exceptions:
+     *
+     * o Tag::KEY_SIZE is not necessary in the input parameters.  If not provided, the
+     *   IKeyMintDevice must deduce the value from the provided key material and add the tag and
+     *   value to the key characteristics.  If Tag::KEY_SIZE is provided, the IKeyMintDevice must
+     *   validate it against the key material.  In the event of a mismatch, importKey must return
+     *   ErrorCode::IMPORT_PARAMETER_MISMATCH.
+     *
+     * o Tag::RSA_PUBLIC_EXPONENT (for RSA keys only) is not necessary in the input parameters.  If
+     *   not provided, the IKeyMintDevice must deduce the value from the provided key material and
+     *   add the tag and value to the key characteristics.  If Tag::RSA_PUBLIC_EXPONENT is provided,
+     *   the IKeyMintDevice must validate it against the key material.  In the event of a
+     *   mismatch, importKey must return ErrorCode::IMPORT_PARAMETER_MISMATCH.
+     *
+     * o Tag::ORIGIN (returned in keyCharacteristics) must have the value KeyOrigin::IMPORTED.
+     *
+     * @param keyParams Key generation parameters are defined as KeyMintDevice tag/value pairs,
+     *        provided in params.
+     *
+     * @param keyFormat The format of the key material to import.  See KeyFormat in keyformat.aidl.
+     *
+     * @param keyData The key material to import, in the format specified in keyFormat.
+     *
+     * @param attestationKey, if provided, specifies the key that must be used to sign the
+     *        attestation certificate.  If `keyParams` does not contain a Tag::ATTESTATION_CHALLENGE
+     *        but `attestationKey` is non-null, the IKeyMintDevice must return
+     *        ErrorCode::INVALID_ARGUMENT.  If the provided AttestationKey does not contain a key
+     *        blob containing an asymmetric key with KeyPurpose::ATTEST_KEY, the IKeyMintDevice must
+     *        return ErrorCode::INCOMPATIBLE_PURPOSE.  If the provided AttestationKey has an empty
+     *        issuer subject name, the IKeyMintDevice must return ErrorCode::INVALID_ARGUMENT.
+     *
+     *        If `attestationKey` is null and `keyParams` contains Tag::ATTESTATION_CHALLENGE but
+     *        the KeyMint implementation does not have factory-provisioned attestation keys, it must
+     *        return ErrorCode::ATTESTATION_KEYS_NOT_PROVISIONED.
+     *
+     * @return The result of key creation.  See KeyCreationResult.aidl.
+     */
+    KeyCreationResult importKey(in KeyParameter[] keyParams, in KeyFormat keyFormat,
+            in byte[] keyData, in @nullable AttestationKey attestationKey);
+
+    /**
+     * Securely imports a key, or key pair, returning a key blob and a description of the imported
+     * key.
+     *
+     * @param wrappedKeyData The wrapped key material to import, as ASN.1 DER-encoded data
+     *        corresponding to the following schema.
+     *
+     *     KeyDescription ::= SEQUENCE(
+     *         keyFormat INTEGER,                   # Values from KeyFormat enum.
+     *         keyParams AuthorizationList,
+     *     )
+     *
+     *     SecureKeyWrapper ::= SEQUENCE(
+     *         version INTEGER,                     # Contains value 0
+     *         encryptedTransportKey OCTET_STRING,
+     *         initializationVector OCTET_STRING,
+     *         keyDescription KeyDescription,
+     *         encryptedKey OCTET_STRING,
+     *         tag OCTET_STRING
+     *     )
+     *
+     *     Where:
+     *
+     *     - keyFormat is an integer from the KeyFormat enum, defining the format of the plaintext
+     *       key material.
+     *     - keyParams is the characteristics of the key to be imported (as with generateKey or
+     *       importKey).  If the secure import is successful, these characteristics must be
+     *       associated with the key exactly as if the key material had been insecurely imported
+     *       with the IKeyMintDevice::importKey.  See KeyCreationResult.aidl for documentation of
+     *       the AuthorizationList schema.
+     *     - encryptedTransportKey is a 256-bit AES key, XORed with a masking key and then encrypted
+     *       with the wrapping key specified by wrappingKeyBlob.
+     *     - keyDescription is a KeyDescription, above.
+     *     - encryptedKey is the key material of the key to be imported, in format keyFormat, and
+     *       encrypted with encryptedEphemeralKey in AES-GCM mode, with the DER-encoded
+     *       representation of keyDescription provided as additional authenticated data.
+     *     - tag is the tag produced by the AES-GCM encryption of encryptedKey.
+     *
+     * So, importWrappedKey does the following:
+     *
+     *     1. Get the private key material for wrappingKeyBlob, verifying that the wrapping key has
+     *        purpose KEY_WRAP, padding mode RSA_OAEP, and digest SHA_2_256, returning the
+     *        error INCOMPATIBLE_PURPOSE, INCOMPATIBLE_PADDING_MODE, or INCOMPATIBLE_DIGEST if any
+     *        of those requirements fail.
+     *     2. Extract the encryptedTransportKey field from the SecureKeyWrapper, and decrypt
+     *        it with the wrapping key.
+     *     3. XOR the result of step 2 with maskingKey.
+     *     4. Use the result of step 3 as an AES-GCM key to decrypt encryptedKey, using the encoded
+     *        value of keyDescription as the additional authenticated data.  Call the result
+     *        "keyData" for the next step.
+     *     5. Perform the equivalent of calling importKey(keyParams, keyFormat, keyData), except
+     *        that the origin tag should be set to SECURELY_IMPORTED.
+     *
+     * @param wrappingKeyBlob The opaque key descriptor returned by generateKey() or importKey().
+     *        This key must have been created with Purpose::WRAP_KEY.
+     *
+     * @param maskingKey The 32-byte value XOR'd with the transport key in the SecureWrappedKey
+     *        structure.
+     *
+     * @param unwrappingParams must contain any parameters needed to perform the unwrapping
+     *        operation.  For example, if the wrapping key is an AES key the block and padding modes
+     *        must be specified in this argument.
+     *
+     * @param passwordSid specifies the password secure ID (SID) of the user that owns the key being
+     *        installed.  If the authorization list in wrappedKeyData contains a
+     *        Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::PASSWORD bit
+     *        set, the constructed key must be bound to the SID value provided by this argument.  If
+     *        the wrappedKeyData does not contain such a tag and value, this argument must be
+     *        ignored.
+     *
+     * @param biometricSid specifies the biometric secure ID (SID) of the user that owns the key
+     *        being installed.  If the authorization list in wrappedKeyData contains a
+     *        Tag::USER_SECURE_ID with a value that has the HardwareAuthenticatorType::FINGERPRINT
+     *        bit set, the constructed key must be bound to the SID value provided by this argument.
+     *        If the wrappedKeyData does not contain such a tag and value, this argument must be
+     *        ignored.
+     *
+     * @return The result of key creation.  See KeyCreationResult.aidl.
+     */
+    KeyCreationResult importWrappedKey(in byte[] wrappedKeyData, in byte[] wrappingKeyBlob,
+            in byte[] maskingKey, in KeyParameter[] unwrappingParams, in long passwordSid,
+            in long biometricSid);
+
+    /**
+     * Upgrades an old key blob.  Keys can become "old" in two ways: IKeyMintDevice can be
+     * upgraded to a new version with an incompatible key blob format, or the system can be updated
+     * to invalidate the OS version (OS_VERSION tag), system patch level (OS_PATCHLEVEL tag),
+     * vendor patch level (VENDOR_PATCH_LEVEL tag), boot patch level (BOOT_PATCH_LEVEL tag) or
+     * other, implementation-defined patch level (keyMint implementers are encouraged to extend
+     * this HAL with a minor version extension to define validatable patch levels for other
+     * images; tags must be defined in the implementer's namespace, starting at 10000).  In either
+     * case, attempts to use an old key blob with begin() must result in IKeyMintDevice returning
+     * ErrorCode::KEY_REQUIRES_UPGRADE.  The caller must use this method to upgrade the key blob.
+     *
+     * The upgradeKey method must examine each version or patch level associated with the key.  If
+     * any one of them is higher than the corresponding current device value upgradeKey() must
+     * return ErrorCode::INVALID_ARGUMENT.  There is one exception: it is always permissible to
+     * "downgrade" from any OS_VERSION number to OS_VERSION 0.  For example, if the key has
+     * OS_VERSION 080001, it is permisible to upgrade the key if the current system version is
+     * 080100, because the new version is larger, or if the current system version is 0, because
+     * upgrades to 0 are always allowed.  If the system version were 080000, however, keyMint must
+     * return ErrorCode::INVALID_ARGUMENT because that value is smaller than 080001.  Values other
+     * than OS_VERSION must never be downgraded.
+     *
+     * Note that Keymaster versions 2 and 3 required that the system and boot images have the same
+     * patch level and OS version.  This requirement is relaxed for 4.0::IKeymasterDevice and
+     * IKeyMintDevice, and the OS version in the boot image footer is no longer used.
+     *
+     * @param keyBlobToUpgrade The opaque descriptor returned by generateKey() or importKey().
+     *
+     * @param upgradeParams A parameter list containing any parameters needed to complete the
+     *        upgrade, including Tag::APPLICATION_ID and Tag::APPLICATION_DATA.
+     *
+     * @return A new key blob that references the same key as keyBlobToUpgrade, but is in the new
+     *         format, or has the new version data.
+     */
+    byte[] upgradeKey(in byte[] keyBlobToUpgrade, in KeyParameter[] upgradeParams);
+
+    /**
+     * Deletes the key, or key pair, associated with the key blob.  Calling this function on
+     * a key with Tag::ROLLBACK_RESISTANCE in its hardware-enforced authorization list must
+     * render the key permanently unusable.  Keys without Tag::ROLLBACK_RESISTANCE may or
+     * may not be rendered unusable.
+     *
+     * @param keyBlob The opaque descriptor returned by generateKey() or importKey();
+     *
+     * @return error See the ErrorCode enum.
+     */
+    void deleteKey(in byte[] keyBlob);
+
+    /**
+     * Deletes all keys in the hardware keystore.  Used when keystore is reset completely.  After
+     * this function is called all keys with Tag::ROLLBACK_RESISTANCE in their hardware-enforced
+     * authorization lists must be rendered permanently unusable.  Keys without
+     * Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
+     */
+    void deleteAllKeys();
+
+    /**
+     * Destroys knowledge of the device's ids.  This prevents all device id attestation in the
+     * future.  The destruction must be permanent so that not even a factory reset will restore the
+     * device ids.
+     *
+     * Device id attestation may be provided only if this method is fully implemented, allowing the
+     * user to permanently disable device id attestation.  If this cannot be guaranteed, the device
+     * must never attest any device ids.
+     *
+     * This is a NOP if device id attestation is not supported.
+     */
+    void destroyAttestationIds();
+
+    /**
+     * Begins a cryptographic operation using the specified key.  If all is well, begin() must
+     * return ErrorCode::OK and create an IKeyMintOperation handle which will be used to perform
+     * the cryptographic operation.
+     *
+     * It is critical that each successful call to begin() be paired with a subsequent call to
+     * finish() or abort() on the resulting IKeyMintOperation, to allow the IKeyMintDevice
+     * implementation to clean up any internal operation state.  The caller's failure to do this may
+     * leak internal state space or other internal resources and may eventually cause begin() to
+     * return ErrorCode::TOO_MANY_OPERATIONS when it runs out of space for operations.  Any result
+     * other than ErrorCode::OK from begin() will not return an IKeyMintOperation (in which case
+     * calling finish() or abort() is neither possible nor necessary). IKeyMintDevice
+     * implementations must support 32 concurrent operations.
+     *
+     * If Tag::APPLICATION_ID or Tag::APPLICATION_DATA were specified during key generation or
+     * import, calls to begin must include those tags with the originally-specified values in the
+     * params argument to this method.  If not, begin() must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * == Authorization Enforcement ==
+     *
+     * The following key authorization parameters must be enforced by the IKeyMintDevice secure
+     * environment if the tags were returned in the "hardwareEnforced" list in the
+     * KeyCharacteristics.
+     *
+     * -- All Key Types --
+     *
+     * The tags in this section apply to all key types.  See below for additional key type-specific
+     * tags.
+     *
+     * o Tag::PURPOSE: The purpose specified in the begin() call must match one of the purposes in
+     *   the key authorizations.  If the specified purpose does not match, begin() must return
+     *   ErrorCode::UNSUPPORTED_PURPOSE.
+     *
+     * o Tag::ACTIVE_DATETIME can only be enforced if a trusted UTC time source is available.  If
+     *   the current date and time is prior to the tag value, begin() must return
+     *   ErrorCode::KEY_NOT_YET_VALID.
+     *
+     * o Tag::ORIGINATION_EXPIRE_DATETIME can only be enforced if a trusted UTC time source is
+     *   available.  If the current date and time is later than the tag value and the purpose is
+     *   KeyPurpose::ENCRYPT or KeyPurpose::SIGN, begin() must return ErrorCode::KEY_EXPIRED.
+     *
+     * o Tag::USAGE_EXPIRE_DATETIME can only be enforced if a trusted UTC time source is
+     *   available.  If the current date and time is later than the tag value and the purpose is
+     *   KeyPurpose::DECRYPT or KeyPurpose::VERIFY, begin() must return ErrorCode::KEY_EXPIRED.
+     *
+     * o Tag::MAX_USES_PER_BOOT must be compared against a secure counter that tracks the uses of
+     *   the key since boot time.  If the count of previous uses exceeds the tag value, begin() must
+     *   return ErrorCode::KEY_MAX_OPS_EXCEEDED.
+     *
+     * o Tag::USER_SECURE_ID must be enforced by this method if and only if the key also has
+     *   Tag::AUTH_TIMEOUT (if it does not have Tag::AUTH_TIMEOUT, the Tag::USER_SECURE_ID
+     *   requirement must be enforced by updateAad(), update() and finish()).  If the key has both,
+     *   then this method must receive a non-empty HardwareAuthToken in the authToken argument.  For
+     *   the auth token to be valid, all of the following have to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The timestamp in the auth token plus the value of the Tag::AUTH_TIMEOUT must be less than
+     *     the current secure timestamp (which is a monotonic timer counting milliseconds since
+     *     boot.)
+     *
+     *   If any of these conditions are not met, begin() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * o Tag::CALLER_NONCE allows the caller to specify a nonce or initialization vector (IV).  If
+     *   the key doesn't have this tag, but the caller provided Tag::NONCE to this method,
+     *   ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
+     *
+     * o Tag::BOOTLOADER_ONLY specifies that only the bootloader may use the key.  If this method is
+     *   called with a bootloader-only key after the bootloader has finished executing, it must
+     *   return ErrorCode::INVALID_KEY_BLOB.  The mechanism for notifying the IKeyMintDevice that
+     *   the bootloader has finished executing is implementation-defined.
+     *
+     * -- RSA Keys --
+     *
+     * All RSA key operations must specify exactly one padding mode in params.  If unspecified or
+     * specified more than once, the begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE.
+     *
+     * RSA signing operations need a digest, as do RSA encryption and decryption operations with
+     * OAEP padding mode.  For those cases, the caller must specify exactly one digest in params.
+     * If unspecified or specified more than once, begin() must return
+     * ErrorCode::UNSUPPORTED_DIGEST.
+     *
+     * Private key operations (KeyPurpose::DECRYPT and KeyPurpose::SIGN) need authorization of
+     * digest and padding, which means that the key authorizations need to contain the specified
+     * values.  If not, begin() must return ErrorCode::INCOMPATIBLE_DIGEST or
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE, as appropriate.
+     *
+     * With the exception of PaddingMode::NONE, all RSA padding modes are applicable only to certain
+     * purposes.  Specifically, PaddingMode::RSA_PKCS1_1_5_SIGN and PaddingMode::RSA_PSS only
+     * support signing, while PaddingMode::RSA_PKCS1_1_5_ENCRYPT and PaddingMode::RSA_OAEP only
+     * support encryption and decryption.  begin() must return ErrorCode::UNSUPPORTED_PADDING_MODE
+     * if the specified mode does not support the specified purpose.
+     *
+     * There are some important interactions between padding modes and digests:
+     *
+     * o PaddingMode::NONE indicates that a "raw" RSA operation is performed.  If signing,
+     *   Digest::NONE is specified for the digest.  No digest is necessary for unpadded encryption
+     *   or decryption.
+     *
+     * o PaddingMode::RSA_PKCS1_1_5_SIGN padding requires a digest.  The digest may be Digest::NONE,
+     *   in which case the KeyMint implementation cannot build a proper PKCS#1 v1.5 signature
+     *   structure, because it cannot add the DigestInfo structure.  Instead, the IKeyMintDevice
+     *   must construct 0x00 || 0x01 || PS || 0x00 || M, where M is the provided message and PS is a
+     *   random padding string at least eight bytes in length.  The size of the RSA key has to be at
+     *   least 11 bytes larger than the message, otherwise begin() must return
+     *   ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * o PaddingMode::RSA_PKCS1_1_1_5_ENCRYPT padding does not require a digest.
+     *
+     * o PaddingMode::RSA_PSS padding requires a digest, which must match one of the padding values
+     *   in the key authorizations, and which may not be Digest::NONE.  begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST if this is not the case.  In addition, the size of the RSA
+     *   key must be at least 2 + D bytes larger than the output size of the digest, where D is the
+     *   size of the digest, in bytes.  Otherwise begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST.  The salt size must be D.
+     *
+     * o PaddingMode::RSA_OAEP padding requires a digest, which must match one of the padding values
+     *   in the key authorizations, and which may not be Digest::NONE.  begin() must return
+     *   ErrorCode::INCOMPATIBLE_DIGEST if this is not the case.  RSA_OAEP padding also requires an
+     *   MGF1 digest, specified with Tag::RSA_OAEP_MGF_DIGEST, which must match one of the MGF1
+     *   padding values in the key authorizations and which may not be Digest::NONE.  begin() must
+     *   return ErrorCode::INCOMPATIBLE_MGF_DIGEST if this is not the case. The OAEP mask generation
+     *   function must be MGF1.
+     *
+     * -- EC Keys --
+     *
+     * Private key operations (KeyPurpose::SIGN) need authorization of digest and padding, which
+     * means that the key authorizations must contain the specified values.  If not, begin() must
+     * return ErrorCode::INCOMPATIBLE_DIGEST.
+     *
+     * -- AES Keys --
+     *
+     * AES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding mode
+     * (Tag::PADDING) in params.  If either value is unspecified or specified more than once,
+     * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
+     * ErrorCode::UNSUPPORTED_PADDING_MODE.  The specified modes must be authorized by the key,
+     * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::GCM, params must specify Tag::MAC_LENGTH, and the specified
+     * value must be a multiple of 8 that is not greater than 128 or less than the value of
+     * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than 128 or
+     * non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For values less
+     * than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * If the block mode is BlockMode::GCM or BlockMode::CTR, the specified padding mode must be
+     * PaddingMode::NONE.  For BlockMode::ECB or BlockMode::CBC, the mode may be PaddingMode::NONE
+     * or PaddingMode::PKCS7.  If the padding mode doesn't meet these conditions, begin() must
+     * return ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::CBC, BlockMode::CTR, or BlockMode::GCM, an initialization
+     * vector or nonce is required.  In most cases, callers shouldn't provide an IV or nonce and the
+     * IKeyMintDevice implementation must generate a random IV or nonce and return it via Tag::NONCE
+     * in outParams.  CBC and CTR IVs are 16 bytes.  GCM nonces are 12 bytes.  If the key
+     * authorizations contain Tag::CALLER_NONCE, then the caller may provide an IV/nonce with
+     * Tag::NONCE in params, which must be of the correct size (if not, return
+     * ErrorCode::INVALID_NONCE).  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED.  If a nonce is not provided when
+     * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+     *
+     * -- 3DES Keys --
+     *
+     * 3DES key operations must specify exactly one block mode (Tag::BLOCK_MODE) and one padding
+     * mode (Tag::PADDING) in params.  If either value is unspecified or specified more than once,
+     * begin() must return ErrorCode::UNSUPPORTED_BLOCK_MODE or
+     * ErrorCode::UNSUPPORTED_PADDING_MODE.  The specified modes must be authorized by the key,
+     * otherwise begin() must return ErrorCode::INCOMPATIBLE_BLOCK_MODE or
+     * ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * If the block mode is BlockMode::CBC, an initialization vector or nonce is required.  In most
+     * cases, callers shouldn't provide an IV or nonce and the IKeyMintDevice implementation must
+     * generate a random IV or nonce and return it via Tag::NONCE in outParams.  CBC IVs are 8
+     * bytes.  If the key authorizations contain Tag::CALLER_NONCE, then the caller may provide an
+     * IV/nonce with Tag::NONCE in params, which must be of the correct size (if not, return
+     * ErrorCode::INVALID_NONCE).  If a nonce is provided when Tag::CALLER_NONCE is not authorized,
+     * begin() must return ErrorCode::CALLER_NONCE_PROHIBITED.  If a nonce is not provided when
+     * Tag::CALLER_NONCE is authorized, IKeyMintDevice must generate a random IV/nonce.
+     *
+     *
+     * -- HMAC keys --
+     *
+     * HMAC key operations must specify Tag::MAC_LENGTH in params.  The specified value must be a
+     * multiple of 8 that is not greater than the digest length or less than the value of
+     * Tag::MIN_MAC_LENGTH in the key authorizations.  For MAC lengths greater than the digest
+     * length or non-multiples of 8, begin() must return ErrorCode::UNSUPPORTED_MAC_LENGTH.  For
+     * values less than the key's minimum length, begin() must return ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * @param purpose The purpose of the operation, one of KeyPurpose::ENCRYPT, KeyPurpose::DECRYPT,
+     *        KeyPurpose::SIGN, KeyPurpose::VERIFY, or KeyPurpose::AGREE_KEY.  Note that for AEAD
+     *        modes, encryption and decryption imply signing and verification, respectively, but
+     *        must be specified as KeyPurpose::ENCRYPT and KeyPurpose::DECRYPT.
+     *
+     * @param keyBlob The opaque key descriptor returned by generateKey() or importKey().  The key
+     *        must have a purpose compatible with purpose and all of its usage requirements must be
+     *        satisfied, or begin() must return an appropriate error code (see above).
+     *
+     * @param params Additional parameters for the operation.  If Tag::APPLICATION_ID or
+     *        Tag::APPLICATION_DATA were provided during generation, they must be provided here, or
+     *        the operation must fail with ErrorCode::INVALID_KEY_BLOB.  For operations that require
+     *        a nonce or IV, on keys that were generated with Tag::CALLER_NONCE, params may
+     *        contain a tag Tag::NONCE.  If Tag::NONCE is provided for a key without
+     *        Tag:CALLER_NONCE, ErrorCode::CALLER_NONCE_PROHIBITED must be returned.
+     *
+     * @param authToken Authentication token.
+     *
+     * @return BeginResult as output, which contains the challenge, KeyParameters which haves
+     *         additional data from the operation initialization, notably to return the IV or nonce
+     *         from operations that generate an IV or nonce, and IKeyMintOperation object pointer
+     *         which is used to perform updateAad(), update(), finish() or abort() operations.
+     */
+    BeginResult begin(in KeyPurpose purpose, in byte[] keyBlob, in KeyParameter[] params,
+            in @nullable HardwareAuthToken authToken);
+
+    /**
+     * Called by client to notify the IKeyMintDevice that the device is now locked, and keys with
+     * the UNLOCKED_DEVICE_REQUIRED tag should no longer be usable.  When this function is called,
+     * the IKeyMintDevice should note the current timestamp, and attempts to use
+     * UNLOCKED_DEVICE_REQUIRED keys must be rejected with Error::DEVICE_LOCKED until an
+     * authentication token with a later timestamp is presented.  If the `passwordOnly' argument is
+     * set to true the sufficiently-recent authentication token must indicate that the user
+     * authenticated with a password, not a biometric.
+     *
+     * Note that the IKeyMintDevice UNLOCKED_DEVICE_REQUIRED semantics are slightly different from
+     * the UNLOCKED_DEVICE_REQUIRED semantics enforced by keystore.  Keystore handles device locking
+     * on a per-user basis.  Because auth tokens do not contain an Android user ID, it's not
+     * possible to replicate the keystore enforcement logic in IKeyMintDevice.  So from the
+     * IKeyMintDevice perspective, any user unlock unlocks all UNLOCKED_DEVICE_REQUIRED keys.
+     * Keystore will continue enforcing the per-user device locking.
+     *
+     * @param passwordOnly specifies whether the device must be unlocked with a password, rather
+     * than a biometric, before UNLOCKED_DEVICE_REQUIRED keys can be used.
+     *
+     * @param timestampToken is used by StrongBox implementations of IKeyMintDevice.  It
+     * provides the StrongBox IKeyMintDevice with a fresh, MACed timestamp which it can use as the
+     * device-lock time, for future comparison against auth tokens when operations using
+     * UNLOCKED_DEVICE_REQUIRED keys are attempted.  Unless the auth token timestamp is newer than
+     * the timestamp in the timestampToken, the device is still considered to be locked.
+     * Crucially, if a StrongBox IKeyMintDevice receives a deviceLocked() call with a timestampToken
+     * timestamp that is less than the timestamp in the last deviceLocked() call, it must ignore the
+     * new timestamp.  TEE IKeyMintDevice implementations will receive an empty timestampToken (zero
+     * values and empty vectors) and should use their own clock as the device-lock time.
+     */
+    void deviceLocked(in boolean passwordOnly, in @nullable TimeStampToken timestampToken);
+
+    /**
+     * Called by client to notify the IKeyMintDevice that the device has left the early boot
+     * state, and that keys with the EARLY_BOOT_ONLY tag may no longer be used.  All attempts to use
+     * an EARLY_BOOT_ONLY key after this method is called must fail with Error::INVALID_KEY_BLOB.
+     */
+    void earlyBootEnded();
+
+    /**
+     * Called by the client to get a wrapped per-boot ephemeral key from a wrapped storage key.
+     * Clients will then use the returned per-boot ephemeral key in place of the wrapped storage
+     * key. Whenever the hardware is presented with a per-boot ephemeral key for an operation, it
+     * must use the storage key associated with that ephemeral key to perform the requested
+     * operation.
+     *
+     * Implementations should return ErrorCode::UNIMPLEMENTED if they don't support wrapped storage
+     * keys.
+     *
+     * Implementations should return ErrorCode::INVALID_ARGUMENT (as a ServiceSpecificException)
+     * if the input key blob doesn't represent a valid long-lived wrapped storage key.
+     *
+     * @param storageKeyBlob is the wrapped storage key for which the client wants a per-boot
+     *        ephemeral key
+     *
+     * @return a buffer containing the per-boot ephemeral keyblob that should henceforth be used in
+     *         place of the input storageKeyBlob
+     */
+    byte[] convertStorageKeyToEphemeral(in byte[] storageKeyBlob);
+
+    /**
+     * Returns KeyMint-enforced parameters associated with the provided key. The returned tags are
+     * a subset of KeyCharacteristics found in the KeyCreationResult returned by generateKey(),
+     * importKey(), or importWrappedKey(). The returned value is a subset, as it does not include
+     * any Keystore-enforced parameters.
+     *
+     * @param keyBlob The opaque descriptor returned by generateKey, importKey or importWrappedKey.
+     *
+     * @param appId An opaque byte string identifying the client.  This value must match the
+     *        Tag::APPLICATION_ID data provided during key generation/import.  Without the correct
+     *        value, it must be computationally infeasible for the secure hardware to obtain the
+     *        key material.
+     *
+     * @param appData An opaque byte string provided by the application.  This value must match the
+     *        Tag::APPLICATION_DATA data provided during key generation/import.  Without the
+     *        correct value, it must be computationally infeasible for the secure hardware to
+     *        obtain the key material.
+     *
+     * @return Characteristics of the generated key. See KeyCreationResult for details.
+     */
+    KeyCharacteristics[] getKeyCharacteristics(
+            in byte[] keyBlob, in byte[] appId, in byte[] appData);
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
new file mode 100644
index 0000000..ce83044
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IKeyMintOperation.aidl
@@ -0,0 +1,282 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.HardwareAuthToken;
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.secureclock.TimeStampToken;
+
+/** @hide */
+@VintfStability
+@SensitiveData
+interface IKeyMintOperation {
+    /**
+     * Provides additional authentication data (AAD) to a cryptographic operation begun with
+     * begin(), provided in the input argument.  This method only applies to AEAD modes.  This
+     * method may be called multiple times, supplying the AAD in chunks, but may not be called after
+     * update() is called.  If updateAad() is called after update(), it must return
+     * ErrorCode::INVALID_TAG.
+     *
+     * If the operation is in an invalid state (was aborted or had an error) update() must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * If this method returns an error code other than ErrorCode::OK, the operation is aborted and
+     * the operation handle must be invalidated.  Any future use of this object must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * == Authorization Enforcement ==
+     *
+     * Key authorization enforcement is performed primarily in begin().  The one exception is the
+     * case where the key has:
+     *
+     * o One or more Tag::USER_SECURE_IDs, and
+     *
+     * o Does not have a Tag::AUTH_TIMEOUT
+     *
+     * In this case, the key requires an authorization per operation, and the update method must
+     * receive a non-null and valid HardwareAuthToken.  For the auth token to be valid, all of the
+     * following has to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The challenge field in the auth token must contain the value returned from
+     *     IKeyMintDevice::begin(), given by the challenge field of the BeginResult structure.
+     *
+     *   If any of these conditions are not met, updateAad() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * The caller must provide the auth token on every call to updateAad(), update() and finish().
+     *
+     *
+     * For GCM encryption, the AEAD tag must be appended to the ciphertext by finish().  During
+     * decryption, the last Tag::MAC_LENGTH bytes of the data provided to the last update call must
+     * be the AEAD tag.  Since a given invocation of update cannot know if it's the last invocation,
+     * it must process all but the tag length and buffer the possible tag data for processing during
+     * finish().
+     *
+     * @param input Additional Authentication Data to be processed.
+     *
+     * @param authToken Authentication token, if provided.
+     *
+     * @param timeStampToken timestamp token, certifies the freshness of an auth token in case
+     *        the security domain of this KeyMint instance has a different clock than the
+     *        authenticator issuing the auth token.
+     *
+     * @return error Returns ErrorCode encountered in keymint as service specific errors. See the
+     *         ErrorCode enum in ErrorCode.aidl.
+     */
+    void updateAad(in byte[] input, in @nullable HardwareAuthToken authToken,
+            in @nullable TimeStampToken timeStampToken);
+
+    /**
+     * Provides data to, and possibly receives output from, an ongoing cryptographic operation begun
+     * with begin().
+     *
+     * If operation is in an invalid state (was aborted or had an error) update() must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * Implementations may choose how much data to return as a result of the update.  This is
+     * only relevant for encryption and decryption operations, because signing returns no data
+     * until finish.  It is recommended to return data as early as possible, rather than buffer it.
+     *
+     * If this method returns an error code other than ErrorCode::OK, the operation is aborted and
+     * the operation handle must be invalidated.  Any future use of the handle, with this method,
+     * finish, or abort, must return ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * == Authorization Enforcement ==
+     *
+     * Key authorization enforcement is performed primarily in IKeyMintDevice::begin().  The one
+     * exception is the case where the key has:
+     *
+     * o One or more Tag::USER_SECURE_IDs, and
+     *
+     * o Does not have a Tag::AUTH_TIMEOUT
+     *
+     * In this case, the key requires an authorization per operation, and the update method must
+     * receive a non-empty and valid HardwareAuthToken.  For the auth token to be valid, all of the
+     * following has to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The challenge field in the auth token must contain the challenge value contained in the
+     *     BeginResult returned from IKeyMintDevice::begin().
+     *
+     *   If any of these conditions are not met, update() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * The caller must provide the auth token on every call to update() and finish().
+     *
+     * -- RSA keys --
+     *
+     * For signing operations with Digest::NONE, this method must accept the entire block to be
+     * signed in a single update.  It may not consume only a portion of the block in these cases.
+     * However, the caller may choose to provide the data in multiple updates, and update() must
+     * accept the data this way as well.  If the caller provides more data to sign than can be used
+     * (length of data exceeds RSA key size), update() must return ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * -- ECDSA keys --
+     *
+     * For signing operations with Digest::NONE, this method must accept the entire block to be
+     * signed in a single update.  This method may not consume only a portion of the block.
+     * However, the caller may choose to provide the data in multiple updates and update() must
+     * accept the data this way as well.  If the caller provides more data to sign than can be used,
+     * the data is silently truncated.  (This differs from the handling of excess data provided in
+     * similar RSA operations.  The reason for this is compatibility with legacy clients.)
+     *
+     * -- AES keys --
+     *
+     * For GCM encryption, the AEAD tag must be appended to the ciphertext by finish().  During
+     * decryption, the last Tag::MAC_LENGTH bytes of the data provided to the last update call must
+     * be the AEAD tag.  Since a given invocation of update cannot know if it's the last invocation,
+     * it must process all but the tag length and buffer the possible tag data for processing during
+     * finish().
+     *
+     * @param input Data to be processed.  update() must consume all input data.
+     *
+     * @param authToken Authentication token. Can be nullable if not provided.
+     *
+     * @param timeStampToken certifies the freshness of an auth token in case the security domain of
+     *        this KeyMint instance has a different clock than the authenticator issuing the auth
+     *        token.
+     *
+     * @return error Returns ErrorCode encountered in keymint as service specific errors. See the
+     *         ErrorCode enum in ErrorCode.aidl.
+     *
+     * @return byte[] The output data, if any.
+     */
+    byte[] update(in byte[] input, in @nullable HardwareAuthToken authToken,
+            in @nullable TimeStampToken timeStampToken);
+
+    /**
+     * Finalizes a cryptographic operation begun with begin() and invalidates the operation.
+     *
+     * This method is the last one called in an operation, so all processed data must be returned.
+     *
+     * Whether it completes successfully or returns an error, this method finalizes the operation.
+     * Any future use of the operation, with finish(), update(), or abort(), must return
+     * ErrorCode::INVALID_OPERATION_HANDLE.
+     *
+     * Signing operations return the signature as the output.
+     *
+     * == Authorization enforcement ==
+     *
+     * Key authorization enforcement is performed primarily in begin().  The exceptions are
+     * authorization per operation keys and confirmation-required keys.
+     *
+     * Authorization per operation keys are the case where the key has one or more
+     * Tag::USER_SECURE_IDs, and does not have a Tag::AUTH_TIMEOUT.  In this case, the key requires
+     * an authorization per operation, and the finish method must receive a non-empty and valid
+     * authToken.  For the auth token to be valid, all of the following has to be true:
+     *
+     *   o The HMAC field must validate correctly.
+     *
+     *   o At least one of the Tag::USER_SECURE_ID values from the key must match at least one of
+     *     the secure ID values in the token.
+     *
+     *   o The key must have a Tag::USER_AUTH_TYPE that matches the auth type in the token.
+     *
+     *   o The challenge field in the auth token must contain the operation challenge.
+     *
+     *   If any of these conditions are not met, update() must return
+     *   ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * The caller must provide the auth token on every call to update() and finish().
+     *
+     * Confirmation-required keys are keys that were generated with
+     * Tag::TRUSTED_CONFIRMATION_REQUIRED.  For these keys, when doing a signing operation the
+     * caller must pass a KeyParameter Tag::CONFIRMATION_TOKEN to finish().  Implementations must
+     * check the confirmation token by computing the 32-byte HMAC-SHA256 over all of the
+     * to-be-signed data, prefixed with the 18-byte UTF-8 encoded string "confirmation token". If
+     * the computed value does not match the Tag::CONFIRMATION_TOKEN parameter, finish() must not
+     * produce a signature and must return ErrorCode::NO_USER_CONFIRMATION.
+     *
+     * -- RSA keys --
+     *
+     * Some additional requirements, depending on the padding mode:
+     *
+     * o PaddingMode::NONE.  For unpadded signing and encryption operations, if the provided data is
+     *   shorter than the key, the data must be zero-padded on the left before signing/encryption.
+     *   If the data is the same length as the key, but numerically larger, finish() must return
+     *   ErrorCode::INVALID_ARGUMENT.  For decryption operations, the data must be exactly as long
+     *   as the key.  Otherwise, return ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * o PaddingMode::RSA_PSS.  For PSS-padded signature operations, the PSS salt length must match
+     *   the size of the PSS digest selected.  The digest specified with Tag::DIGEST in params
+     *   on begin() must be used as the PSS digest algorithm, MGF1 must be used as the mask
+     *   generation function and SHA1 must be used as the MGF1 digest algorithm.
+     *
+     * -- ECDSA keys --
+     *
+     * If the data provided for undigested signing is too long, truncate it.
+     *
+     * -- AES keys --
+     *
+     * Some additional conditions, depending on block mode:
+     *
+     * o BlockMode::ECB or BlockMode::CBC.  If padding is PaddingMode::NONE and the data length is
+     *   not a multiple of the AES block size, finish() must return
+     *   ErrorCode::INVALID_INPUT_LENGTH.  If padding is PaddingMode::PKCS7, pad the data per the
+     *   PKCS#7 specification, including adding an additional padding block if the data is a
+     *   multiple of the block length.
+     *
+     * o BlockMode::GCM.  During encryption, after processing all plaintext, compute the tag
+     *   (Tag::MAC_LENGTH bytes) and append it to the returned ciphertext.  During decryption,
+     *   process the last Tag::MAC_LENGTH bytes as the tag.  If tag verification fails, finish()
+     *   must return ErrorCode::VERIFICATION_FAILED.
+     *
+     * @param input Data to be processed, per the parameters established in the call to begin().
+     *        finish() must consume all provided data or return ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * @param signature The signature to be verified if the purpose specified in the begin() call
+     *        was KeyPurpose::VERIFY.
+     *
+     * @param authToken Authentication token. Can be nullable if not provided.
+     *
+     * @param timestampToken certifies the freshness of an auth token in case the security domain of
+     *        this KeyMint instance has a different clock than the authenticator issuing the auth
+     *        token.
+     *
+     * @param confirmationToken is the confirmation token required by keys with
+     *        Tag::TRUSTED_CONFIRMATION_REQUIRED.
+     *
+     * @return The output data, if any.
+     */
+    byte[] finish(in @nullable byte[] input, in @nullable byte[] signature,
+            in @nullable HardwareAuthToken authToken,
+            in @nullable TimeStampToken timestampToken,
+            in @nullable byte[] confirmationToken);
+
+    /**
+     * Aborts a cryptographic operation begun with IKeyMintDevice::begin(), freeing all internal
+     * resources.  If an operation was finalized, calling updateAad, update, finish, or abort yields
+     * ErrorCode::INVALID_OPERATION_HANDLE. An operation is finalized if finish or abort was called
+     * on it, or if updateAad or update returned an ErrorCode.
+     *
+     * @return error See the ErrorCode enum in ErrorCode.aidl.
+     */
+    void abort();
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
new file mode 100644
index 0000000..a29fb08
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.aidl
@@ -0,0 +1,294 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.DeviceInfo;
+import android.hardware.security.keymint.MacedPublicKey;
+import android.hardware.security.keymint.ProtectedData;
+import android.hardware.security.keymint.RpcHardwareInfo;
+
+/**
+ * An IRemotelyProvisionedComponent is a secure-side component for which certificates can be
+ * remotely provisioned. It provides an interface for generating asymmetric key pairs and then
+ * creating a CertificateRequest that contains the generated public keys, plus other information to
+ * authenticate the request origin. The CertificateRequest can be sent to a server, which can
+ * validate the request and create certificates.
+ *
+ * This interface does not provide any way to use the generated and certified key pairs. It's
+ * intended to be implemented by a HAL service that does other things with keys (e.g. Keymint).
+ *
+ * The root of trust for secure provisioning is something called the "Boot Certificate Chain", or
+ * BCC. The BCC is a chain of public key certificates, represented as COSE_Sign1 objects containing
+ * COSE_Key representations of the public keys. The "root" of the BCC is
+ * a device-unique public key, denoted DK_pub. All public keys in the BCC are device-unique. The
+ * public key from each certificate in the chain is used to sign the next certificate in the
+ * chain. The final, "leaf" certificate contains a public key, denoted KM_pub, whose corresponding
+ * private key, denoted KM_priv, is available for use by the IRemotelyProvisionedComponent.
+ *
+ * BCC Design
+ * ==========
+ *
+ * The BCC is designed to mirror the boot stages of a device, and to prove the content and integrity
+ * of each firmware image. In a proper BCC, each boot stage hashes its own private key with the code
+ * and any relevant configuration parameters of the next stage to produce a key pair for the next
+ * stage. Each stage also uses its own private key to sign the public key of the next stage,
+ * including in the certificate the hash of the next firmware stage, then loads the next stage,
+ * passing the private key and certificate to it in a manner that does not leak the private key to
+ * later boot stages. The BCC root key pair is generated by immutable code (e.g. ROM), from a
+ * device-unique secret. After the device-unique secret is used, it must be made unavailable to any
+ * later boot stage.
+ *
+ * In this way, booting the device incrementally builds a certificate chain that (a) identifies and
+ * validates the integrity of every stage and (b) contains a set of public keys that correspond to
+ * private keys, one known to each stage. Any stage can compute the secrets of all later stages
+ * (given the necessary input), but no stage can compute the secret of any preceding stage. Updating
+ * the firmware or configuration of any stage changes the key pair of that stage, and of all
+ * subsequent stages, and no attacker who compromised the previous version of the updated firmware
+ * can know or predict the post-update key pairs. It is recommended and expected that the BCC is
+ * constructed using the Open Profile for DICE.
+ *
+ * When the provisioning server receives a message signed by KM_priv and containing a BCC that
+ * chains from DK_pub to KM_pub, it can be certain that (barring vulnerabilities in some boot
+ * stage), the CertificateRequest came from the device associated with DK_pub, running the specific
+ * software identified by the certificates in the BCC. If the server has some mechanism for knowing
+ * which the DK_pub values of "valid" devices, it can determine whether signing certificates is
+ * appropriate.
+ *
+ * Degenerate BCCs
+ * ===============
+ *
+ * While a proper BCC, as described above, reflects the complete boot sequence from boot ROM to the
+ * secure area image of the IRemotelyProvisionedComponent, it's also possible to use a "degenerate"
+ * BCC which consists only of a single, self-signed certificate containing the public key of a
+ * hardware-bound key pair. This is an appopriate solution for devices which haven't implemented
+ * everything necessary to produce a proper BCC, but can derive a unique key pair in the secure
+ * area.  In this degenerate case, DK_pub is the same as KM_pub.
+ *
+ * BCC Privacy
+ * ===========
+ *
+ * Because the BCC constitutes an unspoofable, device-unique identifier, special care is taken to
+ * prevent its availability to entities who may wish to track devices. Two precautions are taken:
+ *
+ * 1.  The BCC is never exported from the IRemotelyProvisionedComponent except in encrypted
+ *     form. The portion of the CertificateRequest that contains the BCC is encrypted using an
+ *     Endpoint Encryption Key (EEK).  The EEK is provided in the form of a certificate chain whose
+ *     root must be pre-provisioned into the secure area (hardcoding the roots into the secure area
+ *     firmware image is a recommended approach). Multiple roots may be provisioned. If the provided
+ *     EEK does not chain back to this already-known root, the IRemotelyProvisionedComponent must
+ *     reject it.
+ *
+ * 2.  Precaution 1 above ensures that only an entity with a valid EEK private key can decrypt the
+ *     BCC. To make it feasible to build a provisioning server which cannot use the BCC to track
+ *     devices, the CertificateRequest is structured so that the server can be partitioned into two
+ *     components.  The "decrypter" decrypts the BCC, verifies DK_pub and the device's right to
+ *     receive provisioned certificates, but does not see the public keys to be signed or the
+ *     resulting certificates.  The "certifier" gets informed of the results of the decrypter's
+ *     validation and sees the public keys to be signed and resulting certificates, but does not see
+ *     the BCC.
+ *
+ * Test Mode
+ * =========
+ *
+ * The IRemotelyProvisionedComponent supports a test mode, allowing the generation of test key pairs
+ * and test CertificateRequests. Test keys/requests are annotated as such, and the BCC used for test
+ * CertificateRequests must contain freshly-generated keys, not the real BCC key pairs.
+ * @hide
+ */
+@VintfStability
+interface IRemotelyProvisionedComponent {
+    const int STATUS_FAILED = 1;
+    const int STATUS_INVALID_MAC = 2;
+    const int STATUS_PRODUCTION_KEY_IN_TEST_REQUEST = 3;
+    const int STATUS_TEST_KEY_IN_PRODUCTION_REQUEST = 4;
+    const int STATUS_INVALID_EEK = 5;
+
+    /**
+     * @return info which contains information about the underlying IRemotelyProvisionedComponent
+     *         hardware, such as version number, component name, author name, and supported curve.
+     */
+    RpcHardwareInfo getHardwareInfo();
+
+    /**
+     * generateKeyPair generates a new ECDSA P-256 key pair that can be certified.  Note that this
+     * method only generates ECDSA P-256 key pairs, but the interface can be extended to add methods
+     * for generating keys for other algorithms, if necessary.
+     *
+     * @param in boolean testMode indicates whether the generated key is for testing only. Test keys
+     *        are marked (see the definition of PublicKey in the MacedPublicKey structure) to
+     *        prevent them from being confused with production keys.
+     *
+     * @param out MacedPublicKey macedPublicKey contains the public key of the generated key pair,
+     *        MACed so that generateCertificateRequest can easily verify, without the
+     *        privateKeyHandle, that the contained public key is for remote certification.
+     *
+     * @return data representing a handle to the private key. The format is implementation-defined,
+     *         but note that specific services may define a required format. KeyMint does.
+     */
+    byte[] generateEcdsaP256KeyPair(in boolean testMode, out MacedPublicKey macedPublicKey);
+
+    /**
+     * generateCertificateRequest creates a certificate request to be sent to the provisioning
+     * server.
+     *
+     * @param in boolean testMode indicates whether the generated certificate request is for testing
+     *        only.
+     *
+     * @param in MacedPublicKey[] keysToSign contains the set of keys to certify. The
+     *        IRemotelyProvisionedComponent must validate the MACs on each key.  If any entry in the
+     *        array lacks a valid MAC, the method must return STATUS_INVALID_MAC.
+     *
+     *        If testMode is true, the keysToCertify array must contain only keys flagged as test
+     *        keys. Otherwise, the method must return STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
+     *
+     *        If testMode is false, the keysToCertify array must not contain any keys flagged as
+     *        test keys. Otherwise, the method must return STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
+     *
+     * @param in endpointEncryptionKey contains an X22519 public key which will be used to encrypt
+     *        the BCC. For flexibility, this is represented as a certificate chain, represented as a
+     *        CBOR array of COSE_Sign1 objects, ordered from root to leaf. The leaf contains the
+     *        X25519 encryption key, each other element is an Ed25519 key signing the next in the
+     *        chain. The root is self-signed. An implementor may also choose to use P256 as an
+     *        alternative curve for signing and encryption instead of Curve 25519.
+     *
+     *            EekChain = [ + SignedSignatureKey, SignedEek ]
+     *
+     *            SignedSignatureKey = [              // COSE_Sign1
+     *                protected: bstr .cbor {
+     *                    1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *                },
+     *                unprotected: {},
+     *                payload: bstr .cbor SignatureKeyEd25519 /
+     *                         bstr .cbor SignatureKeyP256,
+     *                signature: bstr PureEd25519(.cbor SignatureKeySignatureInput) /
+     *                           bstr ECDSA(.cbor SignatureKeySignatureInput)
+     *            ]
+     *
+     *            SignatureKeyEd25519 = {                    // COSE_Key
+     *                 1 : 1,                         // Key type : Octet Key Pair
+     *                 3 : AlgorithmEdDSA,            // Algorithm
+     *                 -1 : 6,                        // Curve : Ed25519
+     *                 -2 : bstr                      // Ed25519 public key
+     *            }
+     *
+     *            SignatureKeyP256 = {
+     *                 1 : 2,                         // Key type : EC2
+     *                 3 : AlgorithmES256,            // Algorithm
+     *                 -1 : 1,                        // Curve: P256
+     *                 -2 : bstr,                     // X coordinate
+     *                 -3 : bstr                      // Y coordinate
+     *            }
+     *
+     *            SignatureKeySignatureInput = [
+     *                context: "Signature1",
+     *                body_protected: bstr .cbor {
+     *                    1 : AlgorithmEdDSA / AlgorithmES256,     // Algorithm
+     *                },
+     *                external_aad: bstr .size 0,
+     *                payload: bstr .cbor SignatureKeyEd25519 /
+     *                         bstr .cbor SignatureKeyP256
+     *            ]
+     *
+     *            SignedEek = [                       // COSE_Sign1
+     *                protected: bstr .cbor {
+     *                    1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *                },
+     *                unprotected: {},
+     *                payload: bstr .cbor EekX25519 / .cbor EekP256,
+     *                signature: bstr PureEd25519(.cbor EekSignatureInput) /
+     *                           bstr ECDSA(.cbor EekSignatureInput)
+     *            ]
+     *
+     *            EekX25519 = {            // COSE_Key
+     *                1 : 1,               // Key type : Octet Key Pair
+     *                2 : bstr             // KID : EEK ID
+     *                3 : -25,             // Algorithm : ECDH-ES + HKDF-256
+     *                -1 : 4,              // Curve : X25519
+     *                -2 : bstr            // Ed25519 public key
+     *            }
+     *
+     *            EekP256 = {              // COSE_Key
+     *                1 : 2,               // Key type : EC2
+     *                2 : bstr             // KID : EEK ID
+     *                3 : -25,             // Algorithm : ECDH-ES + HKDF-256
+     *                -1 : 1,              // Curve : P256
+     *                -2 : bstr            // Sender X coordinate
+     *                -3 : bstr            // Sender Y coordinate
+     *            }
+     *
+     *            EekSignatureInput = [
+     *                context: "Signature1",
+     *                body_protected: bstr .cbor {
+     *                    1 : AlgorithmEdDSA / AlgorithmES256,     // Algorithm
+     *                },
+     *                external_aad: bstr .size 0,
+     *                payload: bstr .cbor EekX25519 / .cbor EekP256
+     *            ]
+     *
+     *            AlgorithmES256 = -7
+     *            AlgorithmEdDSA = -8
+     *
+     *        If the contents of endpointEncryptionKey do not match the SignedEek structure above,
+     *        the method must return STATUS_INVALID_EEK.
+     *
+     *        If testMode is true, the method must ignore the length and content of the signatures
+     *        in the chain, which implies that it must not attempt to validate the signature.
+     *
+     *        If testMode is false, the method must validate the chain signatures, and must verify
+     *        that the public key in the root certifictate is in its pre-configured set of
+     *        authorized EEK root keys. If the public key is not in the database, or if signature
+     *        verification fails, the method must return STATUS_INVALID_EEK.
+     *
+     * @param in challenge contains a byte string from the provisioning server that must be signed
+     *        by the secure area. See the description of the 'signature' output parameter for
+     *        details.
+     *
+     * @param out DeviceInfo contains the VerifiedDeviceInfo portion of the DeviceInfo array in
+     *        CertificateRequest. The structure is described within the DeviceInfo.aidl file.
+     *
+     * @param out ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
+     *        authenticate the keysToSign (see keysToSignMac output argument).
+     *
+     * @return The of KeysToSign in the CertificateRequest structure. Specifically, it contains:
+     *
+     *            HMAC-256(EK_mac, .cbor KeysToMacStructure)
+     *
+     *        Where EK_mac is an ephemeral MAC key, found in ProtectedData (see below).  The MACed
+     *        data is the "tag" field of a COSE_Mac0 structure like:
+     *
+     *            MacedKeys = [                            // COSE_Mac0
+     *                protected : bstr .cbor {
+     *                    1 : 5,                           // Algorithm : HMAC-256
+     *                },
+     *                unprotected : {},
+     *                // Payload is PublicKeys from keysToSign argument, in provided order.
+     *                payload: bstr .cbor [ * PublicKey ],
+     *                tag: bstr
+     *            ]
+     *
+     *            KeysToMacStructure = [
+     *                context : "MAC0",
+     *                protected : bstr .cbor { 1 : 5 },    // Algorithm : HMAC-256
+     *                external_aad : bstr .size 0,
+     *                // Payload is PublicKeys from keysToSign argument, in provided order.
+     *                payload : bstr .cbor [ * PublicKey ]
+     *            ]
+     */
+    byte[] generateCertificateRequest(in boolean testMode, in MacedPublicKey[] keysToSign,
+            in byte[] endpointEncryptionCertChain, in byte[] challenge, out DeviceInfo deviceInfo,
+            out ProtectedData protectedData);
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
new file mode 100644
index 0000000..f0df048
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCharacteristics.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameter;
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ * KeyCharacteristics defines the attributes of a key that are enforced by KeyMint, and the security
+ * level (see SecurityLevel.aidl) of that enforcement.
+ *
+ * The `generateKey` `importKey` and `importWrappedKey` methods each return an array of
+ * KeyCharacteristics, specifying the security levels of enforcement and the authorizations
+ * enforced.  Note that enforcement at a given security level means that the semantics of the tag
+ * and value are fully enforced.  See the definition of individual tags for specifications of what
+ * must be enforced.
+ * @hide
+ */
+@VintfStability
+parcelable KeyCharacteristics {
+    /**
+     * The security level enforcing this collection of key properties.
+     */
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
+
+    /**
+     * `authorizations` is a list of key properties that are enforced at this security level.
+     * A key can have different properties enforced by components of different security levels.
+     * For example, some properties are provided by the operating system, which has a
+     * different security level to the IKeyMintDevice.
+     * See the `keyCharacteristics` field in `KeyCreationResult` for more details.
+     */
+    KeyParameter[] authorizations;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
new file mode 100644
index 0000000..fd6bf65
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyCreationResult.aidl
@@ -0,0 +1,200 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.Certificate;
+import android.hardware.security.keymint.KeyCharacteristics;
+
+/**
+ * This structure is returned when a new key is created with generateKey(), importKey() or
+ * importWrappedKey().
+ * @hide
+ */
+@VintfStability
+parcelable KeyCreationResult {
+    /**
+     * `keyBlob` is an descriptor of the generated/imported key key.
+     */
+    byte[] keyBlob;
+
+    /**
+     * `keyCharacteristics` is a description of the generated key in the form of authorization lists
+     * associated with security levels.  The rules that IKeyMintDevice implementations must use for
+     * deciding whether a given tag from `keyParams` argument to the generation/import method should
+     * be returned in `keyCharacteristics` are:
+     *
+     * - If the semantics of the tag are fully enforced by the IKeyMintDevice, without any
+     *   assistance from components running at other security levels, it should be included in an
+     *   entry with the SecurityLevel of the IKeyMintDevice.
+     * - If the semantics of the tag are fully enforced, but with the assistance of components
+     *   running at another SecurityLevel, it should be included in an entry with the minimum
+     *   SecurityLevel of the involved components.  For example if a StrongBox IKeyMintDevice relies
+     *   on a TEE to validate biometric authentication, biometric authentication tags go in an entry
+     *   with SecurityLevel::TRUSTED_ENVIRONMENT.
+     * - If the semantics are not enforced by KeyMint at all, SecurityLevel::KEYSTORE is used to
+     *   indicate that Keystore should enforce.  Note that in Keymaster (predecessor to KeyMint),
+     *   these tags would have been in SecurityLevel::SOFTWARE.
+     */
+    KeyCharacteristics[] keyCharacteristics;
+
+    /**
+     * If the generated/imported key is an asymmetric key, `certificateChain` will contain a chain
+     * of one or more certificates.
+     *
+     * There are a few variations in what is contained in `certificateChain`, depending on whether
+     * the caller requested attestation, whether they provided an attestation key (via the
+     * `attestationKey` parameter of `generateKey()`, `importKey()` or `importWrappedKey()`), and in
+     * the non-attestation case, whether the key can self-sign.
+     *
+     * 1.  Asymmetric key attestation with factory key.  If Tag::ATTESTATION_CHALLENGE is provided
+     *     and the `attestationKey` parameter on the generate/import call is null, the returned
+     *     certificate chain must contain an attestation certificate signed with a factory-
+     *     provisioned attestation key, and the full certificate chain for that factory-provisioned
+     *     attestation key.  Tag::ATTESTATION_APPLICATION_ID must also be provided when the
+     *     ATTESTATION_CHALLENGE is provided, otherwise ATTESTATION_APPLICATION_ID_MISSING will be
+     *     returned.  KeyMint implementations are not required to support factory-provisioned
+     *     attestation keys.
+     *
+     * 2.  Asymmetric key attestation with caller-provided key.  If Tag::ATTESTATION_CHALLENGE is
+     *     provided and the `attestationKey` parameter on the generate/import call is non-null and
+     *     contains the key blob of a key with KeyPurpose::ATTEST_KEY, the returned certificate
+     *     chain must contain only an attestation certificate signed with the specified key.  The
+     *     caller must know the certificate chain for the provided key.  Tag::
+     *     ATTESTATION_APPLICATION_ID must also be provided when the ATTESTATION_CHALLENGE is
+     *     provided, otherwise ATTESTATION_APPLICATION_ID_MISSING will be returned.
+     *
+     * 3.  Asymmetric key non-attestation with signing key.  If Tag::ATTESTATION_CHALLENGE is not
+     *     provided and the generated/imported key has KeyPurpose::SIGN, then the returned
+     *     certificate chain must contain only a single self-signed certificate with no attestation
+     *     extension.  Tag::ATTESTATION_APPLICATION_ID will be ignored if provided.
+     *
+     * 4.  Asymmetric key non-attestation with non-signing key.  If TAG::ATTESTATION_CHALLENGE is
+     *     not provided and the generated/imported key does not have KeyPurpose::SIGN, then the
+     *     returned certificate chain must contain only a single certificate with an empty signature
+     *     and no attestation extension.  Tag::ATTESTATION_APPLICATION_ID will be ignored if
+     *     provided.
+     *
+     * 5.  Symmetric key.  If the generated/imported key is symmetric, the certificate chain must
+     *     return empty, any Tag::ATTESTATION_CHALLENGE or Tag::ATTESTATION_APPLICATION_ID inputs,
+     *     if provided, are ignored.
+     *
+     * In all cases except the symmetric key, the contents of certificate chain must be DER-encoded
+     * X.509 certificates ordered such that each certificate is signed by the subsequent one, up to
+     * the root which must be self-signed (or contain a fake signature in the case of case 4 above).
+     * The first certificate in the chain signs the public key info of the newly-generated or
+     * newly-imported key pair.  In the attestation cases (1 and 2 above), the first certificate
+     * must also satisfy some other requirements:
+     *
+     * o It must have the serial number provided in Tag::CERTIFICATE_SERIAL, or default to 1 if the
+     *   tag is not provided.
+     *
+     * o It must have the subject provided in Tag::CERTIFICATE_SUBJECT, or default to CN="Android
+     *   Keystore Key", if the tag is not provided.
+     *
+     * o It must contain the notBefore and notAfter date-times specified in
+     *   Tag::CERTIFICATE_NOT_BEFORE and Tag::CERTIFICATE_NOT_AFTER, respectively.
+     *
+     * o It must contain a Key Usage extension with:
+     *
+     *    - the digitalSignature bit set iff the attested key has KeyPurpose::SIGN,
+     *    - the dataEncipherment bit set iff the attested key has KeyPurpose::DECRYPT,
+     *    - the keyEncipherment bit set iff the attested key has KeyPurpose::WRAP_KEY,
+     *    - the keyAgreement bit set iff the attested key has KeyPurpose::AGREE_KEY, and
+     *    - the keyCertSignBit set iff the attested key has KeyPurpose::ATTEST_KEY.
+     *
+     * o it must contain a KeyDescription attestation extension with OID 1.3.6.1.4.1.11129.2.1.17.
+     *
+     * The KeyDescription content is defined by the following ASN.1 schema, which is mostly a
+     * straightforward translation of the KeyMint tag/value parameter lists to ASN.1.
+     *
+     * KeyDescription ::= SEQUENCE {
+     *     attestationVersion         INTEGER, # Value 100
+     *     attestationSecurityLevel   SecurityLevel, # See below
+     *     keyMintVersion             INTEGER, # Value 100
+     *     keymintSecurityLevel       SecurityLevel, # See below
+     *     attestationChallenge       OCTET_STRING, # Tag::ATTESTATION_CHALLENGE from attestParams
+     *     uniqueId                   OCTET_STRING, # Empty unless key has Tag::INCLUDE_UNIQUE_ID
+     *     softwareEnforced           AuthorizationList, # See below
+     *     hardwareEnforced           AuthorizationList, # See below
+     * }
+     *
+     * SecurityLevel ::= ENUMERATED {
+     *     Software                   (0),
+     *     TrustedEnvironment         (1),
+     *     StrongBox                  (2),
+     * }
+     *
+     * RootOfTrust ::= SEQUENCE {
+     *     verifiedBootKey            OCTET_STRING,
+     *     deviceLocked               BOOLEAN,
+     *     verifiedBootState          VerifiedBootState,
+     *     # verifiedBootHash must contain 32-byte value that represents the state of all binaries
+     *     # or other components validated by verified boot.  Updating any verified binary or
+     *     # component must cause this value to change.
+     *     verifiedBootHash           OCTET_STRING,
+     * }
+     *
+     * VerifiedBootState ::= ENUMERATED {
+     *     Verified                   (0),
+     *     SelfSigned                 (1),
+     *     Unverified                 (2),
+     *     Failed                     (3),
+     * }
+     *
+     * AuthorizationList ::= SEQUENCE {
+     *     purpose                    [1] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     algorithm                  [2] EXPLICIT INTEGER OPTIONAL,
+     *     keySize                    [3] EXPLICIT INTEGER OPTIONAL,
+     *     digest                     [5] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     padding                    [6] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     ecCurve                    [10] EXPLICIT INTEGER OPTIONAL,
+     *     rsaPublicExponent          [200] EXPLICIT INTEGER OPTIONAL,
+     *     mgfDigest                  [203] EXPLICIT SET OF INTEGER OPTIONAL,
+     *     rollbackResistance         [303] EXPLICIT NULL OPTIONAL,
+     *     earlyBootOnly              [305] EXPLICIT NULL OPTIONAL,
+     *     activeDateTime             [400] EXPLICIT INTEGER OPTIONAL,
+     *     originationExpireDateTime  [401] EXPLICIT INTEGER OPTIONAL,
+     *     usageExpireDateTime        [402] EXPLICIT INTEGER OPTIONAL,
+     *     usageCountLimit            [405] EXPLICIT INTEGER OPTIONAL,
+     *     noAuthRequired             [503] EXPLICIT NULL OPTIONAL,
+     *     userAuthType               [504] EXPLICIT INTEGER OPTIONAL,
+     *     authTimeout                [505] EXPLICIT INTEGER OPTIONAL,
+     *     allowWhileOnBody           [506] EXPLICIT NULL OPTIONAL,
+     *     trustedUserPresenceReq     [507] EXPLICIT NULL OPTIONAL,
+     *     trustedConfirmationReq     [508] EXPLICIT NULL OPTIONAL,
+     *     unlockedDeviceReq          [509] EXPLICIT NULL OPTIONAL,
+     *     creationDateTime           [701] EXPLICIT INTEGER OPTIONAL,
+     *     origin                     [702] EXPLICIT INTEGER OPTIONAL,
+     *     rootOfTrust                [704] EXPLICIT RootOfTrust OPTIONAL,
+     *     osVersion                  [705] EXPLICIT INTEGER OPTIONAL,
+     *     osPatchLevel               [706] EXPLICIT INTEGER OPTIONAL,
+     *     attestationApplicationId   [709] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdBrand         [710] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdDevice        [711] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdProduct       [712] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdSerial        [713] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdImei          [714] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdMeid          [715] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdManufacturer  [716] EXPLICIT OCTET_STRING OPTIONAL,
+     *     attestationIdModel         [717] EXPLICIT OCTET_STRING OPTIONAL,
+     *     vendorPatchLevel           [718] EXPLICIT INTEGER OPTIONAL,
+     *     bootPatchLevel             [719] EXPLICIT INTEGER OPTIONAL,
+     *     deviceUniqueAttestation    [720] EXPLICIT NULL OPTIONAL,
+     * }
+     */
+    Certificate[] certificateChain;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
new file mode 100644
index 0000000..da3d521
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyFormat.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Formats for key import and export.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyFormat {
+    /** X.509 certificate format, for public key export. */
+    X509 = 0,
+    /** PCKS#8 format, asymmetric key pair import. */
+    PKCS8 = 1,
+    /** Raw bytes, for symmetric key import. */
+    RAW = 3,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
new file mode 100644
index 0000000..b82dee6
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyMintHardwareInfo.aidl
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.SecurityLevel;
+
+/**
+ * KeyMintHardwareInfo is the hardware information returned by calling KeyMint getHardwareInfo()
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyMintHardwareInfo {
+    /**
+     * Implementation version of the keymint hardware.  The version number is implementation
+     * defined, and not necessarily globally meaningful.  The version is used to distinguish
+     * between different versions of a given implementation.
+     */
+    int versionNumber;
+
+    /* securityLevel is the security level of the IKeyMintDevice implementation accessed
+     * through this aidl package.  */
+    SecurityLevel securityLevel = SecurityLevel.SOFTWARE;
+
+    /* keyMintName is the name of the IKeyMintDevice implementation.  */
+    @utf8InCpp String keyMintName;
+
+    /* keyMintAuthorName is the name of the author of the IKeyMintDevice implementation
+     *         (organization name, not individual). This name is implementation defined,
+     *         so it can be used to distinguish between different implementations from the
+     *         same author.
+     */
+    @utf8InCpp String keyMintAuthorName;
+
+    /* The timestampTokenRequired is a boolean flag, which when true reflects that IKeyMintDevice
+     * instance will expect a valid TimeStampToken with various operations. This will typically
+     * required by the StrongBox implementations that generally don't have secure clock hardware to
+     * generate timestamp tokens.
+     */
+    boolean timestampTokenRequired;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
new file mode 100644
index 0000000..5840c6b
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyOrigin.aidl
@@ -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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * The origin of a key (or pair), i.e. where it was generated.  Note that ORIGIN can be found in
+ * either the hardware-enforced or software-enforced list for a key, indicating whether the key is
+ * hardware or software-based.  Specifically, a key with GENERATED in the hardware-enforced list
+ * must be guaranteed never to have existed outside the secure hardware.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyOrigin {
+    /** Generated in keyMint.  Should not exist outside the TEE. */
+    GENERATED = 0,
+
+    /** Derived inside keyMint.  Likely exists off-device. */
+    DERIVED = 1,
+
+    /** Imported into keyMint.  Existed as cleartext in Android. */
+    IMPORTED = 2,
+
+    /** Previously used for another purpose that is now obsolete. */
+    RESERVED = 3,
+
+    /**
+     * Securely imported into KeyMint.  Was created elsewhere, and passed securely through Android
+     * to secure hardware.
+     */
+    SECURELY_IMPORTED = 4,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
new file mode 100644
index 0000000..b69e678
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameter.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.KeyParameterValue;
+import android.hardware.security.keymint.Tag;
+
+/**
+ * Identifies the key authorization parameters to be used with keyMint.  This is usually
+ * provided as an array of KeyParameters to IKeyMintDevice or Operation.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyParameter {
+    Tag tag = Tag.INVALID;
+    KeyParameterValue value;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
new file mode 100644
index 0000000..924f402
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyParameterValue.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.Algorithm;
+import android.hardware.security.keymint.BlockMode;
+import android.hardware.security.keymint.Digest;
+import android.hardware.security.keymint.EcCurve;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
+import android.hardware.security.keymint.KeyOrigin;
+import android.hardware.security.keymint.KeyPurpose;
+import android.hardware.security.keymint.PaddingMode;
+import android.hardware.security.keymint.SecurityLevel;
+
+/** @hide */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+union KeyParameterValue {
+    /* Represents an invalid value type. */
+    int invalid;
+
+    /* Enum types */
+    Algorithm algorithm;
+    BlockMode blockMode;
+    PaddingMode paddingMode;
+    Digest digest;
+    EcCurve ecCurve;
+    KeyOrigin origin;
+    KeyPurpose keyPurpose;
+    HardwareAuthenticatorType hardwareAuthenticatorType;
+    SecurityLevel securityLevel;
+
+    /* Other types */
+    boolean boolValue; // Always true, if present.
+    int integer;
+    long longInteger;
+    long dateTime; // In milliseconds from epoch
+
+    byte[] blob;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
new file mode 100644
index 0000000..e141e55
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/KeyPurpose.aidl
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Possible purposes of a key (or pair).
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum KeyPurpose {
+    /* Usable with RSA, 3DES and AES keys. */
+    ENCRYPT = 0,
+
+    /* Usable with RSA, 3DES and AES keys. */
+    DECRYPT = 1,
+
+    /* Usable with RSA, EC and HMAC keys. */
+    SIGN = 2,
+
+    /* Usable with RSA, EC and HMAC keys. */
+    VERIFY = 3,
+
+    /* 4 is reserved */
+
+    /* Usable with wrapping keys. */
+    WRAP_KEY = 5,
+
+    /* Key Agreement, usable with EC keys. */
+    AGREE_KEY = 6,
+
+    /* Usable as an attestation signing key.  Keys with this purpose must not have any other
+     * purpose. */
+    ATTEST_KEY = 7,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
new file mode 100644
index 0000000..62a48e9
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/MacedPublicKey.aidl
@@ -0,0 +1,59 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * MacedPublicKey contains a CBOR-encoded public key, MACed by an IRemotelyProvisionedComponent, to
+ * prove that the key pair was generated by that component.
+ * @hide
+ */
+@VintfStability
+parcelable MacedPublicKey {
+    /**
+     * key is a COSE_Mac0 structure containing the new public key.  It's MACed by a key available
+     * only to the secure environment, as proof that the public key was generated by that
+     * environment. In CDDL, assuming the contained key is a P-256 public key:
+     *
+     *     MacedPublicKey = [                     // COSE_Mac0
+     *         protected: bstr .cbor { 1 : 5},    // Algorithm : HMAC-256
+     *         unprotected: { },
+     *         payload : bstr .cbor PublicKey,
+     *         tag : bstr HMAC-256(K_mac, MAC_structure)
+     *     ]
+     *
+     *     PublicKey = {               // COSE_Key
+     *         1 : 2,                  // Key type : EC2
+     *         3 : -8                  // Algorithm : ES256
+     *         -1 : 6,                 // Curve : P256
+     *         -2 : bstr               // X coordinate, little-endian
+     *         -3 : bstr               // Y coordinate, little-endian
+     *         ? -70000 : nil          // Presence indicates this is a test key.  If set, K_mac is
+     *                                 // all zeros.
+     *     },
+     *
+     *     MAC_structure = [
+     *         context : "MAC0",
+     *         protected : bstr .cbor { 1 : 5 },
+     *         external_aad : bstr .size 0,
+     *         payload : bstr .cbor PublicKey
+     *     ]
+     *
+     * if a non-P256 public key were contained, the contents of the PublicKey map would change a
+     * little; see RFC 8152 for details.
+     */
+    byte[] macedKey;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
new file mode 100644
index 0000000..e71a9c9
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/PaddingMode.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Padding modes that may be applied to plaintext for encryption operations.  This list includes
+ * padding modes for both symmetric and asymmetric algorithms.  Note that implementations should not
+ * provide all possible combinations of algorithm and padding, only the
+ * cryptographically-appropriate pairs.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum PaddingMode {
+    NONE = 1, /* deprecated */
+    RSA_OAEP = 2,
+    RSA_PSS = 3,
+    RSA_PKCS1_1_5_ENCRYPT = 4,
+    RSA_PKCS1_1_5_SIGN = 5,
+    PKCS7 = 64,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
new file mode 100644
index 0000000..24cdbc1
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/ProtectedData.aidl
@@ -0,0 +1,197 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * ProtectedData contains the encrypted BCC and the ephemeral MAC key used to
+ * authenticate the keysToSign (see keysToSignMac output argument).
+ * @hide
+ */
+@VintfStability
+parcelable ProtectedData {
+    /**
+     * ProtectedData is a COSE_Encrypt structure, specified by the following CDDL
+     *
+     *     ProtectedData = [               // COSE_Encrypt
+     *         protected: bstr .cbor {
+     *             1 : 3                   // Algorithm : AES-GCM 256
+     *         },
+     *         unprotected: {
+     *             5 : bstr .size 12       // IV
+     *         },
+     *         ciphertext: bstr,           // AES-GCM-256(K, .cbor ProtectedDataPayload)
+     *         recipients : [
+     *             [                       // COSE_Recipient
+     *                 protected : bstr .cbor {
+     *                     1 : -25         // Algorithm : ECDH-ES + HKDF-256
+     *                 },
+     *                 unprotected : {
+     *                     -1 : PubKeyX25519 / PubKeyEcdhP256  // Of the sender
+     *                     4 : bstr,       // KID : EEK ID
+     *                 },
+     *                 ciphertext : nil
+     *             ]
+     *         ]
+     *     ]
+     *
+     *     K = HKDF-256(ECDH(EEK_pub, Ephemeral_priv), Context)
+     *
+     *     Context = [                     // COSE_KDF_Context
+     *         AlgorithmID : 3             // AES-GCM 256
+     *         PartyUInfo : [
+     *             identity : bstr "client"
+     *             nonce : bstr .size 0,
+     *             other : bstr            // Ephemeral pubkey
+     *         ],
+     *         PartyVInfo : [
+     *             identity : bstr "server",
+     *             nonce : bstr .size 0,
+     *             other : bstr            // EEK pubkey
+     *         ],
+     *         SuppPubInfo : [
+     *             256,                    // Output key length
+     *             protected : bstr .size 0
+     *         ]
+     *     ]
+     *
+     *     ProtectedDataPayload [
+     *         SignedMac,
+     *         Bcc,
+     *         ? AdditionalDKSignatures,
+     *     ]
+     *     AdditionalDKSignatures = {
+     *         + SignerName => DKCertChain
+     *     }
+     *
+     *     SignerName = tstr
+     *
+     *     DKCertChain = [
+     *         2* Certificate                      // Root -> Leaf.  Root is the vendor
+     *                                             // self-signed cert, leaf contains DK_pub
+     *     ]
+     *
+     *     Certificate = COSE_Sign1 of a public key
+     *
+     *     SignedMac = [                                  // COSE_Sign1
+     *         bstr .cbor {                               // Protected params
+     *             1 : AlgorithmEdDSA / AlgorithmES256,   // Algorithm
+     *         },
+     *         {},                   // Unprotected params
+     *         bstr .size 32,                  // MAC key
+     *         bstr PureEd25519(KM_priv, .cbor SignedMac_structure) /
+     *              ECDSA(KM_priv, bstr .cbor SignedMac_structure)
+     *     ]
+     *
+     *     SignedMac_structure = [
+     *         "Signature1",
+     *         bstr .cbor {                               // Protected params
+     *             1 : AlgorithmEdDSA / AlgorithmES256,   // Algorithm
+     *         },
+     *         bstr .cbor SignedMacAad
+     *         bstr .size 32                              // MAC key
+     *     ]
+     *
+     *     SignedMacAad = [
+     *         challenge : bstr,
+     *         VerifiedDeviceInfo,
+     *         tag: bstr                 // This is the tag from COSE_Mac0 of
+     *                                   // KeysToCertify, to tie the key set to
+     *                                   // the signature.
+     *     ]
+     *
+     *     Bcc = [
+     *         PubKeyEd25519 / PubKeyECDSA256, // DK_pub
+     *         + BccEntry,                     // Root -> leaf (KM_pub)
+     *     ]
+     *
+     *     BccPayload = {                     // CWT
+     *         1 : tstr,                      // Issuer
+     *         2 : tstr,                      // Subject
+     *         // See the Open Profile for DICE for details on these fields.
+     *         ? -4670545 : bstr,             // Code Hash
+     *         ? -4670546 : bstr,             // Code Descriptor
+     *         ? -4670547 : bstr,             // Configuration Hash
+     *         ? -4670548 : bstr .cbor {      // Configuration Descriptor
+     *             ? -70002 : tstr,           // Component name
+     *             ? -70003 : int,            // Firmware version
+     *             ? -70004 : null,           // Resettable
+     *         },
+     *         ? -4670549 : bstr,             // Authority Hash
+     *         ? -4670550 : bstr,             // Authority Descriptor
+     *         ? -4670551 : bstr,             // Mode
+     *         -4670552 : bstr .cbor PubKeyEd25519 /
+     *                    bstr .cbor PubKeyECDSA256   // Subject Public Key
+     *         -4670553 : bstr                // Key Usage
+     *     }
+     *
+     *     BccEntry = [                                  // COSE_Sign1 (untagged)
+     *         protected : bstr .cbor {
+     *             1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *         },
+     *         unprotected: {},
+     *         payload: bstr .cbor BccPayload,
+     *         signature: bstr .cbor PureEd25519(SigningKey, bstr .cbor BccEntryInput) /
+     *                    bstr .cbor ECDSA(SigningKey, bstr .cbor BccEntryInput)
+     *         // See RFC 8032 for details of how to encode the signature value for Ed25519.
+     *     ]
+     *
+     *     BccEntryInput = [
+     *         context: "Signature1",
+     *         protected: bstr .cbor {
+     *             1 : AlgorithmEdDSA / AlgorithmES256,  // Algorithm
+     *         },
+     *         external_aad: bstr .size 0,
+     *         payload: bstr .cbor BccPayload
+     *     ]
+     *
+     *     VerifiedDeviceInfo = DeviceInfo  // See DeviceInfo.aidl
+     *
+     *     PubKeyX25519 = {                 // COSE_Key
+     *          1 : 1,                      // Key type : Octet Key Pair
+     *         -1 : 4,                      // Curve : X25519
+     *         -2 : bstr                    // Sender X25519 public key
+     *     }
+     *
+     *     PubKeyEd25519 = {                // COSE_Key
+     *         1 : 1,                         // Key type : octet key pair
+     *         3 : AlgorithmEdDSA,            // Algorithm : EdDSA
+     *         4 : 2,                         // Ops: Verify
+     *         -1 : 6,                        // Curve : Ed25519
+     *         -2 : bstr                      // X coordinate, little-endian
+     *     }
+     *
+     *     PubKeyEcdhP256 = {              // COSE_Key
+     *          1 : 2,      // Key type : EC2
+     *          -1 : 1,     // Curve : P256
+     *          -2 : bstr   // Sender X coordinate
+     *          -3 : bstr   // Sender Y coordinate
+     *     }
+     *
+     *     PubKeyECDSA256 = {                 // COSE_Key
+     *         1 : 2,                         // Key type : EC2
+     *         3 : AlgorithmES256,            // Algorithm : ECDSA w/ SHA-256
+     *         4 : 2,                         // Ops: Verify
+     *         -1 : 1,                        // Curve: P256
+     *         -2 : bstr,                     // X coordinate
+     *         -3 : bstr                      // Y coordinate
+     *     }
+     *
+     *     AlgorithmES256 = -7
+     *     AlgorithmEdDSA = -8
+     */
+    byte[] protectedData;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
new file mode 100644
index 0000000..d297f87
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/RpcHardwareInfo.aidl
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * RpcHardwareInfo is the hardware information returned by calling RemotelyProvisionedComponent
+ * getHardwareInfo()
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable RpcHardwareInfo {
+    const int CURVE_NONE = 0;
+    const int CURVE_P256 = 1;
+    const int CURVE_25519 = 2;
+
+    /**
+     * Implementation version of the remotely provisioned component hardware.  The version number is
+     * implementation defined, and not necessarily globally meaningful.  The version is used to
+     * distinguish between different versions of a given implementation.
+     */
+    int versionNumber;
+
+    /**
+     * rpcAuthorName is the name of the author of the IRemotelyProvisionedComponent implementation
+     * (organization name, not individual). This name is implementation defined, so it can be used
+     * to distinguish between different implementations from the same author.
+     */
+    @utf8InCpp String rpcAuthorName;
+
+    /**
+     * supportedEekCurve returns an int representing which curve is supported for validating
+     * signatures over the Endpoint Encryption Key certificate chain and for using the corresponding
+     * signed encryption key in ECDH. Only one curve should be supported, with preference for 25519
+     * if it's available. These values are defined as constants above.
+     *
+     * CURVE_NONE is made the default to help ensure that an implementor doesn't accidentally forget
+     * to provide the correct information here, as the VTS tests will check to make certain that
+     * a passing implementation does not provide CURVE_NONE.
+     */
+    int supportedEekCurve = CURVE_NONE;
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
new file mode 100644
index 0000000..80c63b2
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/SecurityLevel.aidl
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * Device security levels.  These enum values are used in two ways:
+ *
+ * 1.  Returned from IKeyMintDevice::getHardwareInfo to identify the security level of the
+ *     IKeyMintDevice.  This characterizes the sort of environment in which the KeyMint
+ *     implementation runs, and therefore the security of its operations.
+ *
+ * 2.  Associated with individual KeyMint authorization Tags in KeyCharacteristics or in attestation
+ *     certificates.  This specifies the security level of the weakest environment involved in
+ *     enforcing that particular tag, i.e. the sort of security environment an attacker would have
+ *     to subvert in order to break the enforcement of that tag.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum SecurityLevel {
+    /**
+     * The SOFTWARE security level represents a KeyMint implementation that runs in an Android
+     * process, or a tag enforced by such an implementation.  An attacker who can compromise that
+     * process, or obtain root, or subvert the kernel on the device can defeat it.
+     *
+     * Note that the distinction between SOFTWARE and KEYSTORE is only relevant on-device.  For
+     * attestation purposes, these categories are combined into the software-enforced authorization
+     * list.
+     */
+    SOFTWARE = 0,
+
+    /**
+     * The TRUSTED_ENVIRONMENT security level represents a KeyMint implementation that runs in an
+     * isolated execution environment that is securely isolated from the code running on the kernel
+     * and above, and which satisfies the requirements specified in CDD 9.11.1 [C-1-2]. An attacker
+     * who completely compromises Android, including the Linux kernel, does not have the ability to
+     * subvert it.  An attacker who can find an exploit that gains them control of the trusted
+     * environment, or who has access to the physical device and can mount a sophisticated hardware
+     * attack, may be able to defeat it.
+     */
+    TRUSTED_ENVIRONMENT = 1,
+
+    /**
+     * The STRONGBOX security level represents a KeyMint implementation that runs in security
+     * hardware that satisfies the requirements specified in CDD 9.11.2.  Roughly speaking, these
+     * are discrete, security-focus computing environments that are hardened against physical and
+     * side channel attack, and have had their security formally validated by a competent
+     * penetration testing lab.
+     */
+    STRONGBOX = 2,
+
+    /**
+     * KeyMint implementations must never return the KEYSTORE security level from getHardwareInfo.
+     * It is used to specify tags that are not enforced by the IKeyMintDevice, but are instead
+     * to be enforced by Keystore.  An attacker who can subvert the keystore process or gain root or
+     * subvert the kernel can prevent proper enforcement of these tags.
+     *
+     *
+     * Note that the distinction between SOFTWARE and KEYSTORE is only relevant on-device.  When
+     * KeyMint generates an attestation certificate, these categories are combined into the
+     * software-enforced authorization list.
+     */
+    KEYSTORE = 100
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
new file mode 100644
index 0000000..b28ebcb
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl
@@ -0,0 +1,980 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.keymint;
+
+import android.hardware.security.keymint.TagType;
+
+/**
+ * Tag specifies various kinds of tags that can be set in KeyParameter to identify what kind of
+ * data are stored in KeyParameter.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum Tag {
+    /**
+     * Tag::INVALID should never be set.  It means you hit an error.
+     */
+    INVALID = 0,
+
+    /**
+     * Tag::PURPOSE specifies the set of purposes for which the key may be used.  Possible values
+     * are defined in the KeyPurpose enumeration.
+     *
+     * This tag is repeatable; keys may be generated with multiple values, although an operation has
+     * a single purpose.  When begin() is called to start an operation, the purpose of the operation
+     * is specified.  If the purpose specified for the operation is not authorized by the key (the
+     * key didn't have a corresponding Tag::PURPOSE provided during generation/import), the
+     * operation must fail with ErrorCode::INCOMPATIBLE_PURPOSE.
+     *
+     * Must be hardware-enforced.
+     */
+    PURPOSE = TagType.ENUM_REP | 1,
+
+    /**
+     * Tag::ALGORITHM specifies the cryptographic algorithm with which the key is used.  This tag
+     * must be provided to generateKey and importKey, and must be specified in the wrapped key
+     * provided to importWrappedKey.
+     *
+     * Must be hardware-enforced.
+     */
+    ALGORITHM = TagType.ENUM | 2,
+
+    /**
+     * Tag::KEY_SIZE specifies the size, in bits, of the key, measuring in the normal way for the
+     * key's algorithm.  For example, for RSA keys, Tag::KEY_SIZE specifies the size of the public
+     * modulus.  For AES keys it specifies the length of the secret key material.  For 3DES keys it
+     * specifies the length of the key material, not counting parity bits (though parity bits must
+     * be provided for import, etc.).  Since only three-key 3DES keys are supported, 3DES
+     * Tag::KEY_SIZE must be 168.
+     *
+     * Must be hardware-enforced.
+     */
+    KEY_SIZE = TagType.UINT | 3,
+
+    /**
+     * Tag::BLOCK_MODE specifies the block cipher mode(s) with which the key may be used.  This tag
+     * is only relevant to AES and 3DES keys.  Possible values are defined by the BlockMode enum.
+     *
+     * This tag is repeatable for key generation/import.  For AES and 3DES operations the caller
+     * must specify a Tag::BLOCK_MODE in the params argument of begin().  If the mode is missing or
+     * the specified mode is not in the modes specified for the key during generation/import, the
+     * operation must fail with ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+     *
+     * Must be hardware-enforced.
+     */
+    BLOCK_MODE = TagType.ENUM_REP | 4,
+
+    /**
+     * Tag::DIGEST specifies the digest algorithms that may be used with the key to perform signing
+     * and verification operations.  This tag is relevant to RSA, ECDSA and HMAC keys.  Possible
+     * values are defined by the Digest enum.
+     *
+     * This tag is repeatable for key generation/import.  For signing and verification operations,
+     * the caller must specify a digest in the params argument of begin().  If the digest is missing
+     * or the specified digest is not in the digests associated with the key, the operation must
+     * fail with ErrorCode::INCOMPATIBLE_DIGEST.
+     *
+     * Must be hardware-enforced.
+     */
+    DIGEST = TagType.ENUM_REP | 5,
+
+    /**
+     * Tag::PADDING specifies the padding modes that may be used with the key.  This tag is relevant
+     * to RSA, AES and 3DES keys.  Possible values are defined by the PaddingMode enum.
+     *
+     * PaddingMode::RSA_OAEP and PaddingMode::RSA_PKCS1_1_5_ENCRYPT are used only for RSA
+     * encryption/decryption keys and specify RSA OAEP padding and RSA PKCS#1 v1.5 randomized
+     * padding, respectively.  PaddingMode::RSA_PSS and PaddingMode::RSA_PKCS1_1_5_SIGN are used
+     * only for RSA signing/verification keys and specify RSA PSS padding and RSA PKCS#1 v1.5
+     * deterministic padding, respectively.
+     *
+     * PaddingMode::NONE may be used with either RSA, AES or 3DES keys.  For AES or 3DES keys, if
+     * PaddingMode::NONE is used with block mode ECB or CBC and the data to be encrypted or
+     * decrypted is not a multiple of the AES block size in length, the call to finish() must fail
+     * with ErrorCode::INVALID_INPUT_LENGTH.
+     *
+     * PaddingMode::PKCS7 may only be used with AES and 3DES keys, and only with ECB and CBC modes.
+     *
+     * In any case, if the caller specifies a padding mode that is not usable with the key's
+     * algorithm, the generation or import method must return ErrorCode::INCOMPATIBLE_PADDING_MODE.
+     *
+     * This tag is repeatable.  A padding mode must be specified in the call to begin().  If the
+     * specified mode is not authorized for the key, the operation must fail with
+     * ErrorCode::INCOMPATIBLE_BLOCK_MODE.
+     *
+     * Must be hardware-enforced.
+     */
+    PADDING = TagType.ENUM_REP | 6,
+
+    /**
+     * Tag::CALLER_NONCE specifies that the caller can provide a nonce for nonce-requiring
+     * operations.  This tag is boolean, so the possible values are true (if the tag is present) and
+     * false (if the tag is not present).
+     *
+     * This tag is used only for AES and 3DES keys, and is only relevant for CBC, CTR and GCM block
+     * modes.  If the tag is not present in a key's authorization list, implementations must reject
+     * any operation that provides Tag::NONCE to begin() with ErrorCode::CALLER_NONCE_PROHIBITED.
+     *
+     * Must be hardware-enforced.
+     */
+    CALLER_NONCE = TagType.BOOL | 7,
+
+    /**
+     * Tag::MIN_MAC_LENGTH specifies the minimum length of MAC that can be requested or verified
+     * with this key for HMAC keys and AES keys that support GCM mode.
+     *
+     * This value is the minimum MAC length, in bits.  It must be a multiple of 8 bits.  For HMAC
+     * keys, the value must be least 64 and no more than 512.  For GCM keys, the value must be at
+     * least 96 and no more than 128.  If the provided value violates these requirements,
+     * generateKey() or importKey() must return ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH.
+     *
+     * Must be hardware-enforced.
+     */
+    MIN_MAC_LENGTH = TagType.UINT | 8,
+
+    // Tag 9 reserved
+
+    /**
+     * Tag::EC_CURVE specifies the elliptic curve.  Possible values are defined in the EcCurve
+     * enumeration.
+     *
+     * Must be hardware-enforced.
+     */
+    EC_CURVE = TagType.ENUM | 10,
+
+    /**
+     * Tag::RSA_PUBLIC_EXPONENT specifies the value of the public exponent for an RSA key pair.
+     * This tag is relevant only to RSA keys, and is required for all RSA keys.
+     *
+     * The value is a 64-bit unsigned integer that satisfies the requirements of an RSA public
+     * exponent.  This value must be a prime number.  IKeyMintDevice implementations must support
+     * the value 2^16+1 and may support other reasonable values.  If no exponent is specified or if
+     * the specified exponent is not supported, key generation must fail with
+     * ErrorCode::INVALID_ARGUMENT.
+     *
+     * Must be hardware-enforced.
+     */
+    RSA_PUBLIC_EXPONENT = TagType.ULONG | 200,
+
+    // Tag 201 reserved
+
+    /**
+     * Tag::INCLUDE_UNIQUE_ID is specified during key generation to indicate that an attestation
+     * certificate for the generated key should contain an application-scoped and time-bounded
+     * device-unique ID.  See Tag::UNIQUE_ID.
+     *
+     * Must be hardware-enforced.
+     */
+    INCLUDE_UNIQUE_ID = TagType.BOOL | 202,
+
+    /**
+     * Tag::RSA_OAEP_MGF_DIGEST specifies the MGF1 digest algorithms that may be used with RSA
+     * encryption/decryption with OAEP padding.  Possible values are defined by the Digest enum.
+     *
+     * This tag is repeatable for key generation/import.  RSA cipher operations with OAEP padding
+     * must specify an MGF1 digest in the params argument of begin(). If this tag is missing or the
+     * specified digest is not in the MGF1 digests associated with the key then begin operation must
+     * fail with ErrorCode::INCOMPATIBLE_MGF_DIGEST.
+     *
+     * Must be hardware-enforced.
+     */
+    RSA_OAEP_MGF_DIGEST = TagType.ENUM_REP | 203,
+
+    // Tag 301 reserved
+
+    /**
+     * Tag::BOOTLOADER_ONLY specifies only the bootloader can use the key.
+     *
+     * Any attempt to use a key with Tag::BOOTLOADER_ONLY from the Android system must fail with
+     * ErrorCode::INVALID_KEY_BLOB.
+     *
+     * Must be hardware-enforced.
+     */
+    BOOTLOADER_ONLY = TagType.BOOL | 302,
+
+    /**
+     * Tag::ROLLBACK_RESISTANCE specifies that the key has rollback resistance, meaning that when
+     * deleted with deleteKey() or deleteAllKeys(), the key is guaranteed to be permanently deleted
+     * and unusable.  It's possible that keys without this tag could be deleted and then restored
+     * from backup.
+     *
+     * This tag is specified by the caller during key generation or import to require.  If the
+     * IKeyMintDevice cannot guarantee rollback resistance for the specified key, it must return
+     * ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE.  IKeyMintDevice implementations are not
+     * required to support rollback resistance.
+     *
+     * Must be hardware-enforced.
+     */
+    ROLLBACK_RESISTANCE = TagType.BOOL | 303,
+
+    // Reserved for future use.
+    HARDWARE_TYPE = TagType.ENUM | 304,
+
+    /**
+     * Keys tagged with EARLY_BOOT_ONLY may only be used during early boot, until
+     * IKeyMintDevice::earlyBootEnded() is called.  Early boot keys may be created after
+     * early boot.  Early boot keys may not be imported at all, if Tag::EARLY_BOOT_ONLY is
+     * provided to IKeyMintDevice::importKey, the import must fail with
+     * ErrorCode::EARLY_BOOT_ENDED.
+     */
+    EARLY_BOOT_ONLY = TagType.BOOL | 305,
+
+    /**
+     * Tag::ACTIVE_DATETIME specifies the date and time at which the key becomes active, in
+     * milliseconds since Jan 1, 1970.  If a key with this tag is used prior to the specified date
+     * and time, IKeyMintDevice::begin() must return ErrorCode::KEY_NOT_YET_VALID;
+     *
+     * Need not be hardware-enforced.
+     */
+    ACTIVE_DATETIME = TagType.DATE | 400,
+
+    /**
+     * Tag::ORIGINATION_EXPIRE_DATETIME specifies the date and time at which the key expires for
+     * signing and encryption purposes.  After this time, any attempt to use a key with
+     * KeyPurpose::SIGN or KeyPurpose::ENCRYPT provided to begin() must fail with
+     * ErrorCode::KEY_EXPIRED.
+     *
+     * The value is a 64-bit integer representing milliseconds since January 1, 1970.
+     *
+     * Need not be hardware-enforced.
+     */
+    ORIGINATION_EXPIRE_DATETIME = TagType.DATE | 401,
+
+    /**
+     * Tag::USAGE_EXPIRE_DATETIME specifies the date and time at which the key expires for
+     * verification and decryption purposes.  After this time, any attempt to use a key with
+     * KeyPurpose::VERIFY or KeyPurpose::DECRYPT provided to begin() must fail with
+     * ErrorCode::KEY_EXPIRED.
+     *
+     * The value is a 64-bit integer representing milliseconds since January 1, 1970.
+     *
+     * Need not be hardware-enforced.
+     */
+    USAGE_EXPIRE_DATETIME = TagType.DATE | 402,
+
+    /**
+     * Tag::MIN_SECONDS_BETWEEN_OPS specifies the minimum amount of time that elapses between
+     * allowed operations using a key.  This can be used to rate-limit uses of keys in contexts
+     * where unlimited use may enable brute force attacks.
+     *
+     * The value is a 32-bit integer representing seconds between allowed operations.
+     *
+     * When a key with this tag is used in an operation, the IKeyMintDevice must start a timer
+     * during the finish() or abort() call.  Any call to begin() that is received before the timer
+     * indicates that the interval specified by Tag::MIN_SECONDS_BETWEEN_OPS has elapsed must fail
+     * with ErrorCode::KEY_RATE_LIMIT_EXCEEDED.  This implies that the IKeyMintDevice must keep a
+     * table of use counters for keys with this tag.  Because memory is often limited, this table
+     * may have a fixed maximum size and KeyMint may fail operations that attempt to use keys with
+     * this tag when the table is full.  The table must accommodate at least 8 in-use keys and
+     * aggressively reuse table slots when key minimum-usage intervals expire.  If an operation
+     * fails because the table is full, KeyMint returns ErrorCode::TOO_MANY_OPERATIONS.
+     *
+     * Must be hardware-enforced.
+     *
+     * TODO(b/191738660): Remove in KeyMint V2. Currently only used for FDE.
+     */
+    MIN_SECONDS_BETWEEN_OPS = TagType.UINT | 403,
+
+    /**
+     * Tag::MAX_USES_PER_BOOT specifies the maximum number of times that a key may be used between
+     * system reboots.  This is another mechanism to rate-limit key use.
+     *
+     * The value is a 32-bit integer representing uses per boot.
+     *
+     * When a key with this tag is used in an operation, a key-associated counter must be
+     * incremented during the begin() call.  After the key counter has exceeded this value, all
+     * subsequent attempts to use the key must fail with ErrorCode::MAX_OPS_EXCEEDED, until the
+     * device is restarted.  This implies that the IKeyMintDevice must keep a table of use
+     * counters for keys with this tag.  Because KeyMint memory is often limited, this table can
+     * have a fixed maximum size and KeyMint can fail operations that attempt to use keys with
+     * this tag when the table is full.  The table needs to accommodate at least 8 keys.  If an
+     * operation fails because the table is full, IKeyMintDevice must
+     * ErrorCode::TOO_MANY_OPERATIONS.
+     *
+     * Must be hardware-enforced.
+     */
+    MAX_USES_PER_BOOT = TagType.UINT | 404,
+
+    /**
+     * Tag::USAGE_COUNT_LIMIT specifies the number of times that a key may be used. This can be
+     * used to limit the use of a key.
+     *
+     * The value is a 32-bit integer representing the current number of attempts left.
+     *
+     * When initializing a limited use key, the value of this tag represents the maximum usage
+     * limit for that key. After the key usage is exhausted, the key blob should be invalidated by
+     * finish() call. Any subsequent attempts to use the key must result in a failure with
+     * ErrorCode::INVALID_KEY_BLOB returned by IKeyMintDevice.
+     *
+     * At this point, if the caller specifies count > 1, it is not expected that any TEE will be
+     * able to enforce this feature in the hardware due to limited resources of secure
+     * storage. In this case, the tag with the value of maximum usage must be added to the key
+     * characteristics with SecurityLevel::KEYSTORE by the IKeyMintDevice.
+     *
+     * On the other hand, if the caller specifies count = 1, some TEEs may have the ability
+     * to enforce this feature in the hardware with its secure storage. If the IKeyMintDevice
+     * implementation can enforce this feature, the tag with value = 1 must be added to the key
+     * characteristics with the SecurityLevel of the IKeyMintDevice. If the IKeyMintDevice can't
+     * enforce this feature even when the count = 1, the tag must be added to the key
+     * characteristics with the SecurityLevel::KEYSTORE.
+     *
+     * When the key is attested, this tag with the same value must also be added to the attestation
+     * record. This tag must have the same SecurityLevel as the tag that is added to the key
+     * characteristics.
+     */
+    USAGE_COUNT_LIMIT = TagType.UINT | 405,
+
+    /**
+     * Tag::USER_ID specifies the ID of the Android user that is permitted to use the key.
+     *
+     * Must not be hardware-enforced.
+     */
+    USER_ID = TagType.UINT | 501,
+
+    /**
+     * Tag::USER_SECURE_ID specifies that a key may only be used under a particular secure user
+     * authentication state.  This tag is mutually exclusive with Tag::NO_AUTH_REQUIRED.
+     *
+     * The value is a 64-bit integer specifying the authentication policy state value which must be
+     * present in the userId or authenticatorId field of a HardwareAuthToken provided to begin(),
+     * update(), or finish().  If a key with Tag::USER_SECURE_ID is used without a HardwareAuthToken
+     * with the matching userId or authenticatorId, the IKeyMintDevice must return
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * Tag::USER_SECURE_ID interacts with Tag::AUTH_TIMEOUT in a very important way.  If
+     * Tag::AUTH_TIMEOUT is present in the key's characteristics then the key is a "timeout-based"
+     * key, and may only be used if the difference between the current time when begin() is called
+     * and the timestamp in the HardwareAuthToken is less than the value in Tag::AUTH_TIMEOUT * 1000
+     * (the multiplier is because Tag::AUTH_TIMEOUT is in seconds, but the HardwareAuthToken
+     * timestamp is in milliseconds).  Otherwise the IKeyMintDevice must return
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * If Tag::AUTH_TIMEOUT is not present, then the key is an "auth-per-operation" key.  In this
+     * case, begin() must not require a HardwareAuthToken with appropriate contents.  Instead,
+     * update() and finish() must receive a HardwareAuthToken with Tag::USER_SECURE_ID value in
+     * userId or authenticatorId fields, and the current operation's operation handle in the
+     * challenge field.  Otherwise the IKeyMintDevice must return
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * This tag is repeatable.  If repeated, and any one of the values matches the HardwareAuthToken
+     * as described above, the key is authorized for use.  Otherwise the operation must fail with
+     * ErrorCode::KEY_USER_NOT_AUTHENTICATED.
+     *
+     * Must be hardware-enforced.
+     */
+    USER_SECURE_ID = TagType.ULONG_REP | 502,
+
+    /**
+     * Tag::NO_AUTH_REQUIRED specifies that no authentication is required to use this key.  This tag
+     * is mutually exclusive with Tag::USER_SECURE_ID.
+     *
+     * Must be hardware-enforced.
+     */
+    NO_AUTH_REQUIRED = TagType.BOOL | 503,
+
+    /**
+     * Tag::USER_AUTH_TYPE specifies the types of user authenticators that may be used to authorize
+     * this key.
+     *
+     * The value is one or more values from HardwareAuthenticatorType, ORed together.
+     *
+     * When IKeyMintDevice is requested to perform an operation with a key with this tag, it must
+     * receive a HardwareAuthToken and one or more bits must be set in both the HardwareAuthToken's
+     * authenticatorType field and the Tag::USER_AUTH_TYPE value.  That is, it must be true that
+     *
+     *    (token.authenticatorType & tag_user_auth_type) != 0
+     *
+     * where token.authenticatorType is the authenticatorType field of the HardwareAuthToken and
+     * tag_user_auth_type is the value of Tag:USER_AUTH_TYPE.
+     *
+     * Must be hardware-enforced.
+     */
+    USER_AUTH_TYPE = TagType.ENUM | 504,
+
+    /**
+     * Tag::AUTH_TIMEOUT specifies the time in seconds for which the key is authorized for use,
+     * after user authentication.  If
+     * Tag::USER_SECURE_ID is present and this tag is not, then the key requires authentication for
+     * every usage (see begin() for the details of the authentication-per-operation flow).
+     *
+     * The value is a 32-bit integer specifying the time in seconds after a successful
+     * authentication of the user specified by Tag::USER_SECURE_ID with the authentication method
+     * specified by Tag::USER_AUTH_TYPE that the key can be used.
+     *
+     * Must be hardware-enforced.
+     */
+    AUTH_TIMEOUT = TagType.UINT | 505,
+
+    /**
+     * Tag::ALLOW_WHILE_ON_BODY specifies that the key may be used after authentication timeout if
+     * device is still on-body (requires on-body sensor).
+     *
+     * Cannot be hardware-enforced.
+     */
+    ALLOW_WHILE_ON_BODY = TagType.BOOL | 506,
+
+    /**
+     * TRUSTED_USER_PRESENCE_REQUIRED is an optional feature that specifies that this key must be
+     * unusable except when the user has provided proof of physical presence.  Proof of physical
+     * presence must be a signal that cannot be triggered by an attacker who doesn't have one of:
+     *
+     *    a) Physical control of the device or
+     *
+     *    b) Control of the secure environment that holds the key.
+     *
+     * For instance, proof of user identity may be considered proof of presence if it meets the
+     * requirements.  However, proof of identity established in one security domain (e.g. TEE) does
+     * not constitute proof of presence in another security domain (e.g. StrongBox), and no
+     * mechanism analogous to the authentication token is defined for communicating proof of
+     * presence across security domains.
+     *
+     * Some examples:
+     *
+     *     A hardware button hardwired to a pin on a StrongBox device in such a way that nothing
+     *     other than a button press can trigger the signal constitutes proof of physical presence
+     *     for StrongBox keys.
+     *
+     *     Fingerprint authentication provides proof of presence (and identity) for TEE keys if the
+     *     TEE has exclusive control of the fingerprint scanner and performs fingerprint matching.
+     *
+     *     Password authentication does not provide proof of presence to either TEE or StrongBox,
+     *     even if TEE or StrongBox does the password matching, because password input is handled by
+     *     the non-secure world, which means an attacker who has compromised Android can spoof
+     *     password authentication.
+     *
+     * Note that no mechanism is defined for delivering proof of presence to an IKeyMintDevice,
+     * except perhaps as implied by an auth token.  This means that KeyMint must be able to check
+     * proof of presence some other way.  Further, the proof of presence must be performed between
+     * begin() and the first call to update() or finish().  If the first update() or the finish()
+     * call is made without proof of presence, the keyMint method must return
+     * ErrorCode::PROOF_OF_PRESENCE_REQUIRED and abort the operation.  The caller must delay the
+     * update() or finish() call until proof of presence has been provided, which means the caller
+     * must also have some mechanism for verifying that the proof has been provided.
+     *
+     * Only one operation requiring TUP may be in flight at a time.  If begin() has already been
+     * called on one key with TRUSTED_USER_PRESENCE_REQUIRED, and another begin() comes in for that
+     * key or another with TRUSTED_USER_PRESENCE_REQUIRED, KeyMint must return
+     * ErrorCode::CONCURRENT_PROOF_OF_PRESENCE_REQUESTED.
+     *
+     * Must be hardware-enforced.
+     */
+    TRUSTED_USER_PRESENCE_REQUIRED = TagType.BOOL | 507,
+
+    /**
+     * Tag::TRUSTED_CONFIRMATION_REQUIRED is only applicable to keys with KeyPurpose SIGN, and
+     * specifies that this key must not be usable unless the user provides confirmation of the data
+     * to be signed.  Confirmation is proven to keyMint via an approval token.  See the authToken
+     * parameter of begin(), as well as the ConfirmationUI HAL.
+     *
+     * If an attempt to use a key with this tag does not have a cryptographically valid
+     * token provided to finish() or if the data provided to update()/finish() does not
+     * match the data described in the token, keyMint must return NO_USER_CONFIRMATION.
+     *
+     * Must be hardware-enforced.
+     */
+    TRUSTED_CONFIRMATION_REQUIRED = TagType.BOOL | 508,
+
+    /**
+     * Tag::UNLOCKED_DEVICE_REQUIRED specifies that the key may only be used when the device is
+     * unlocked, as reported to KeyMint via authToken operation parameter and the
+     * IKeyMintDevice::deviceLocked() method
+     *
+     * Must be hardware-enforced (but is also keystore-enforced on a per-user basis: see the
+     * deviceLocked() documentation).
+     */
+    UNLOCKED_DEVICE_REQUIRED = TagType.BOOL | 509,
+
+    /**
+     * Tag::APPLICATION_ID.  When provided to generateKey or importKey, this tag specifies data
+     * that is necessary during all uses of the key.  In particular, calls to exportKey() and
+     * getKeyCharacteristics() must provide the same value to the clientId parameter, and calls to
+     * begin() must provide this tag and the same associated data as part of the inParams set.  If
+     * the correct data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * The content of this tag must be bound to the key cryptographically, meaning it must not be
+     * possible for an adversary who has access to all of the secure world secrets but does not have
+     * access to the tag content to decrypt the key without brute-forcing the tag content, which
+     * applications can prevent by specifying sufficiently high-entropy content.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    APPLICATION_ID = TagType.BYTES | 601,
+
+    /*
+     * Semantically unenforceable tags, either because they have no specific meaning or because
+     * they're informational only.
+     */
+
+    /**
+     * Tag::APPLICATION_DATA.  When provided to generateKey or importKey, this tag specifies data
+     * that is necessary during all uses of the key.  In particular, calls to begin() and
+     * exportKey() must provide the same value to the appData parameter, and calls to begin must
+     * provide this tag and the same associated data as part of the inParams set.  If the correct
+     * data is not provided, the method must return ErrorCode::INVALID_KEY_BLOB.
+     *
+     * The content of this tag must be bound to the key cryptographically, meaning it must not be
+     * possible for an adversary who has access to all of the secure world secrets but does not have
+     * access to the tag content to decrypt the key without brute-forcing the tag content, which
+     * applications can prevent by specifying sufficiently high-entropy content.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    APPLICATION_DATA = TagType.BYTES | 700,
+
+    /**
+     * Tag::CREATION_DATETIME specifies the date and time the key was created, in milliseconds since
+     * January 1, 1970.  This tag is optional and informational only, and not enforced by anything.
+     *
+     * Must be in the software-enforced list, if provided.
+     */
+    CREATION_DATETIME = TagType.DATE | 701,
+
+    /**
+     * Tag::ORIGIN specifies where the key was created, if known.  This tag must not be specified
+     * during key generation or import, and must be added to the key characteristics by the
+     * IKeyMintDevice.  The possible values are defined in the KeyOrigin enum.
+     *
+     * Must be hardware-enforced.
+     */
+    ORIGIN = TagType.ENUM | 702,
+
+    // 703 is unused.
+
+    /**
+     * Tag::ROOT_OF_TRUST specifies the root of trust associated with the key used by verified boot
+     * to validate the system.  It describes the boot key, verified boot state, boot hash, and
+     * whether device is locked.  This tag is never provided to or returned from KeyMint in the
+     * key characteristics.  It exists only to define the tag for use in the attestation record.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ROOT_OF_TRUST = TagType.BYTES | 704,
+
+    /**
+     * Tag::OS_VERSION specifies the system OS version with which the key may be used.  This tag is
+     * never sent to the IKeyMintDevice, but is added to the hardware-enforced authorization list
+     * by the TA.  Any attempt to use a key with a Tag::OS_VERSION value different from the
+     * currently-running OS version must cause begin(), getKeyCharacteristics() or exportKey() to
+     * return ErrorCode::KEY_REQUIRES_UPGRADE.  See upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form MMmmss, where MM is the major version number,
+     * mm is the minor version number, and ss is the sub-minor version number.  For example, for a
+     * key generated on Android version 4.0.3, the value would be 040003.
+     *
+     * The IKeyMintDevice HAL must read the current OS version from the system property
+     * ro.build.version.release and deliver it to the secure environment when the HAL is first
+     * loaded (mechanism is implementation-defined).  The secure environment must not accept another
+     * version until after the next boot.  If the content of ro.build.version.release has additional
+     * version information after the sub-minor version number, it must not be included in
+     * Tag::OS_VERSION.  If the content is non-numeric, the secure environment must use 0 as the
+     * system version.
+     *
+     * Must be hardware-enforced.
+     */
+    OS_VERSION = TagType.UINT | 705,
+
+    /**
+     * Tag::OS_PATCHLEVEL specifies the system security patch level with which the key may be used.
+     * This tag is never sent to the keyMint TA, but is added to the hardware-enforced
+     * authorization list by the TA.  Any attempt to use a key with a Tag::OS_PATCHLEVEL value
+     * different from the currently-running system patchlevel must cause begin(),
+     * getKeyCharacteristics() or exportKey() to return ErrorCode::KEY_REQUIRES_UPGRADE.  See
+     * upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMM, where YYYY is the four-digit year of
+     * the last update and MM is the two-digit month of the last update.  For example, for a key
+     * generated on an Android device last updated in December 2015, the value would be 201512.
+     *
+     * The IKeyMintDevice HAL must read the current system patchlevel from the system property
+     * ro.build.version.security_patch and deliver it to the secure environment when the HAL is
+     * first loaded (mechanism is implementation-defined).  The secure environment must not accept
+     * another patchlevel until after the next boot.
+     *
+     * Must be hardware-enforced.
+     */
+    OS_PATCHLEVEL = TagType.UINT | 706,
+
+    /**
+     * Tag::UNIQUE_ID specifies a unique, time-based identifier.  This tag is never provided to or
+     * returned from KeyMint in the key characteristics.  It exists only to define the tag for use
+     * in the attestation record.
+     *
+     * When a key with Tag::INCLUDE_UNIQUE_ID is attested, the unique ID is added to the attestation
+     * record.  The value is a 128-bit hash that is unique per device and per calling application,
+     * and changes monthly and on most password resets.  It is computed with:
+     *
+     *    HMAC_SHA256(T || C || R, HBK)
+     *
+     * Where:
+     *
+     *    T is the "temporal counter value", computed by dividing the value of
+     *      Tag::CREATION_DATETIME by 2592000000, dropping any remainder.  T changes every 30 days
+     *      (2592000000 = 30 * 24 * 60 * 60 * 1000).
+     *
+     *    C is the value of Tag::ATTESTATION_APPLICATION_ID that is provided to attested key
+     *      generation/import operations.
+     *
+     *    R is 1 if Tag::RESET_SINCE_ID_ROTATION was provided to attested key generation/import or 0
+     *      if the tag was not provided.
+     *
+     *    HBK is a unique hardware-bound secret known to the secure environment and never revealed
+     *    by it.  The secret must contain at least 128 bits of entropy and be unique to the
+     *    individual device (probabilistic uniqueness is acceptable).
+     *
+     *    HMAC_SHA256 is the HMAC function, with SHA-2-256 as the hash.
+     *
+     * The output of the HMAC function must be truncated to 128 bits.
+     *
+     * Must be hardware-enforced.
+     */
+    UNIQUE_ID = TagType.BYTES | 707,
+
+    /**
+     * Tag::ATTESTATION_CHALLENGE is used to deliver a "challenge" value to the attested key
+     * generation/import methods, which must place the value in the KeyDescription SEQUENCE of the
+     * attestation extension.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_CHALLENGE = TagType.BYTES | 708,
+
+    /**
+     * Tag::ATTESTATION_APPLICATION_ID identifies the set of applications which may use a key, used
+     * only with attested key generation/import operations.
+     *
+     * The content of Tag::ATTESTATION_APPLICATION_ID is a DER-encoded ASN.1 structure, with the
+     * following schema:
+     *
+     * AttestationApplicationId ::= SEQUENCE {
+     *     packageInfoRecords SET OF PackageInfoRecord,
+     *     signatureDigests   SET OF OCTET_STRING,
+     * }
+     *
+     * PackageInfoRecord ::= SEQUENCE {
+     *     packageName        OCTET_STRING,
+     *     version            INTEGER,
+     * }
+     *
+     * See system/security/keystore/keystore_attestation_id.cpp for details of construction.
+     * IKeyMintDevice implementers do not need to create or parse the ASN.1 structure, but only
+     * copy the tag value into the attestation record.  The DER-encoded string must not exceed 1 KiB
+     * in length.
+     *
+     * Cannot be hardware-enforced.
+     */
+    ATTESTATION_APPLICATION_ID = TagType.BYTES | 709,
+
+    /**
+     * Tag::ATTESTATION_ID_BRAND provides the device's brand name, as returned by Build.BRAND in
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_BRAND = TagType.BYTES | 710,
+
+    /**
+     * Tag::ATTESTATION_ID_DEVICE provides the device's device name, as returned by Build.DEVICE in
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_DEVICE = TagType.BYTES | 711,
+
+    /**
+     * Tag::ATTESTATION_ID_PRODUCT provides the device's product name, as returned by Build.PRODUCT
+     * in Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_PRODUCT = TagType.BYTES | 712,
+
+    /**
+     * Tag::ATTESTATION_ID_SERIAL the device's serial number.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_SERIAL = TagType.BYTES | 713,
+
+    /**
+     * Tag::ATTESTATION_ID_IMEI provides the IMEIs for all radios on the device to attested key
+     * generation/import operations.  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_IMEI = TagType.BYTES | 714,
+
+    /**
+     * Tag::ATTESTATION_ID_MEID provides the MEIDs for all radios on the device to attested key
+     * generation/import operations.  This field must be set only when requesting attestation of the
+     * device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MEID = TagType.BYTES | 715,
+
+    /**
+     * Tag::ATTESTATION_ID_MANUFACTURER provides the device's manufacturer name, as returned by
+     * Build.MANUFACTURER in Android, to attested key generation/import operations.  This field must
+     * be set only when requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MANUFACTURER = TagType.BYTES | 716,
+
+    /**
+     * Tag::ATTESTATION_ID_MODEL provides the device's model name, as returned by Build.MODEL in
+     * Android, to attested key generation/import operations.  This field must be set only when
+     * requesting attestation of the device's identifiers.
+     *
+     * If the device does not support ID attestation (or destroyAttestationIds() was previously
+     * called and the device can no longer attest its IDs), any key attestation request that
+     * includes this tag must fail with ErrorCode::CANNOT_ATTEST_IDS.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    ATTESTATION_ID_MODEL = TagType.BYTES | 717,
+
+    /**
+     * Tag::VENDOR_PATCHLEVEL specifies the vendor image security patch level with which the key may
+     * be used.  This tag is never sent to the keyMint TA, but is added to the hardware-enforced
+     * authorization list by the TA.  Any attempt to use a key with a Tag::VENDOR_PATCHLEVEL value
+     * different from the currently-running system patchlevel must cause begin(),
+     * getKeyCharacteristics() or exportKey() to return ErrorCode::KEY_REQUIRES_UPGRADE.  See
+     * upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMMDD, where YYYY is the four-digit year of
+     * the last update, MM is the two-digit month and DD is the two-digit day of the last
+     * update.  For example, for a key generated on an Android device last updated on June 5, 2018,
+     * the value would be 20180605.
+     *
+     * The IKeyMintDevice HAL must read the current vendor patchlevel from the system property
+     * ro.vendor.build.security_patch and deliver it to the secure environment when the HAL is first
+     * loaded (mechanism is implementation-defined).  The secure environment must not accept another
+     * patchlevel until after the next boot.
+     *
+     * Must be hardware-enforced.
+     */
+    VENDOR_PATCHLEVEL = TagType.UINT | 718,
+
+    /**
+     * Tag::BOOT_PATCHLEVEL specifies the boot image (kernel) security patch level with which the
+     * key may be used.  This tag is never sent to the keyMint TA, but is added to the
+     * hardware-enforced authorization list by the TA.  Any attempt to use a key with a
+     * Tag::BOOT_PATCHLEVEL value different from the currently-running system patchlevel must
+     * cause begin(), getKeyCharacteristics() or exportKey() to return
+     * ErrorCode::KEY_REQUIRES_UPGRADE.  See upgradeKey() for details.
+     *
+     * The value of the tag is an integer of the form YYYYMMDD, where YYYY is the four-digit year of
+     * the last update, MM is the two-digit month and DD is the two-digit day of the last
+     * update.  For example, for a key generated on an Android device last updated on June 5, 2018,
+     * the value would be 20180605.  If the day is not known, 00 may be substituted.
+     *
+     * During each boot, the bootloader must provide the patch level of the boot image to the secure
+     * environment (mechanism is implementation-defined).
+     *
+     * Must be hardware-enforced.
+     */
+    BOOT_PATCHLEVEL = TagType.UINT | 719,
+
+    /**
+     * DEVICE_UNIQUE_ATTESTATION is an argument to IKeyMintDevice::attested key generation/import
+     * operations.  It indicates that attestation using a device-unique key is requested, rather
+     * than a batch key. When a device-unique key is used, the returned chain should contain two or
+     * three certificates.
+     *
+     * In case the chain contains two certificates, they should be:
+     *    * The attestation certificate, containing the attestation extension, as described in
+     *      KeyCreationResult.aidl.
+     *    * A self-signed root certificate, signed by the device-unique key.
+     *
+     * In case the chain contains three certificates, they should be:
+     *    * The attestation certificate, containing the attestation extension, as described in
+     *      KeyCreationResult.aidl, signed by the device-unique key.
+     *    * An intermediate certificate, containing the public portion of the device-unique key.
+     *    * A self-signed root certificate, signed by a dedicated key, certifying the
+     *      intermediate. Ideally, the dedicated key would be the same for all StrongBox
+     *      instances of the same manufacturer to ease validation.
+     *
+     * No additional chained certificates are provided. Only SecurityLevel::STRONGBOX
+     * IKeyMintDevices may support device-unique attestations.  SecurityLevel::TRUSTED_ENVIRONMENT
+     * IKeyMintDevices must return ErrorCode::INVALID_ARGUMENT if they receive
+     * DEVICE_UNIQUE_ATTESTATION.
+     * SecurityLevel::STRONGBOX IKeyMintDevices need not support DEVICE_UNIQUE_ATTESTATION, and
+     * return ErrorCode::CANNOT_ATTEST_IDS if they do not support it.
+     *
+     * The caller needs to obtain the device-unique keys out-of-band and compare them against the
+     * key used to sign the self-signed root certificate.
+     * To ease this process, the IKeyMintDevice implementation should include, both in the subject
+     * and issuer fields of the self-signed root, the unique identifier of the device. Using the
+     * unique identifier will make it straightforward for the caller to link a device to its key.
+     *
+     * IKeyMintDevice implementations that support device-unique attestation MUST add the
+     * DEVICE_UNIQUE_ATTESTATION tag to device-unique attestations.
+     */
+    DEVICE_UNIQUE_ATTESTATION = TagType.BOOL | 720,
+
+    /**
+     * IDENTITY_CREDENTIAL_KEY is never used by IKeyMintDevice, is not a valid argument to key
+     * generation or any operation, is never returned by any method and is never used in a key
+     * attestation.  It is used in attestations produced by the IIdentityCredential HAL when that
+     * HAL attests to Credential Keys.  IIdentityCredential produces KeyMint-style attestations.
+     */
+    IDENTITY_CREDENTIAL_KEY = TagType.BOOL | 721,
+
+    /**
+     * To prevent keys from being compromised if an attacker acquires read access to system / kernel
+     * memory, some inline encryption hardware supports protecting storage encryption keys in
+     * hardware without software having access to or the ability to set the plaintext keys.
+     * Instead, software only sees wrapped version of these keys.
+     *
+     * STORAGE_KEY is used to denote that a key generated or imported is a key used for storage
+     * encryption. Keys of this type can either be generated or imported or secure imported using
+     * keyMint. The convertStorageKeyToEphemeral() method of IKeyMintDevice can be used to re-wrap
+     * storage key with a per-boot ephemeral key wrapped key once the key characteristics are
+     * enforced.
+     *
+     * Keys with this tag cannot be used for any operation within keyMint.
+     * ErrorCode::INVALID_OPERATION is returned when a key with Tag::STORAGE_KEY is provided to
+     * begin().
+     */
+    STORAGE_KEY = TagType.BOOL | 722,
+
+    /**
+     * OBSOLETE: Do not use. See IKeyMintOperation.updateAad instead.
+     * TODO(b/191738660): Remove in KeyMint v2.
+     */
+    ASSOCIATED_DATA = TagType.BYTES | 1000,
+
+    /**
+     * Tag::NONCE is used to provide or return a nonce or Initialization Vector (IV) for AES-GCM,
+     * AES-CBC, AES-CTR, or 3DES-CBC encryption or decryption.  This tag is provided to begin during
+     * encryption and decryption operations.  It is only provided to begin if the key has
+     * Tag::CALLER_NONCE.  If not provided, an appropriate nonce or IV must be randomly generated by
+     * KeyMint and returned from begin.
+     *
+     * The value is a blob, an arbitrary-length array of bytes.  Allowed lengths depend on the mode:
+     * GCM nonces are 12 bytes in length; AES-CBC and AES-CTR IVs are 16 bytes in length, 3DES-CBC
+     * IVs are 8 bytes in length.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    NONCE = TagType.BYTES | 1001,
+
+    /**
+     * Tag::MAC_LENGTH provides the requested length of a MAC or GCM authentication tag, in bits.
+     *
+     * The value is the MAC length in bits.  It must be a multiple of 8 and at least as large as the
+     * value of Tag::MIN_MAC_LENGTH associated with the key.  Otherwise, begin() must return
+     * ErrorCode::INVALID_MAC_LENGTH.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    MAC_LENGTH = TagType.UINT | 1003,
+
+    /**
+     * Tag::RESET_SINCE_ID_ROTATION specifies whether the device has been factory reset since the
+     * last unique ID rotation.  Used for key attestation.
+     *
+     * Must never appear in KeyCharacteristics.
+     */
+    RESET_SINCE_ID_ROTATION = TagType.BOOL | 1004,
+
+    /**
+     * OBSOLETE: Do not use. See the authToken parameter for IKeyMintDevice::begin and for
+     * IKeyMintOperation methods instead.
+     *
+     * TODO(b/191738660): Delete when keystore1 is deleted.
+     */
+    CONFIRMATION_TOKEN = TagType.BYTES | 1005,
+
+    /**
+     * Tag::CERTIFICATE_SERIAL specifies the serial number to be assigned to the attestation
+     * certificate to be generated for the given key.  This parameter should only be passed to
+     * keyMint in the attestation parameters during generateKey() and importKey().  If not provided,
+     * the serial shall default to 1.
+     */
+    CERTIFICATE_SERIAL = TagType.BIGNUM | 1006,
+
+    /**
+     * Tag::CERTIFICATE_SUBJECT the certificate subject.  The value is a DER encoded X509 NAME.
+     * This value is used when generating a self signed certificates.  This tag may be specified
+     * during generateKey and importKey. If not provided the subject name shall default to
+     * CN="Android Keystore Key".
+     */
+    CERTIFICATE_SUBJECT = TagType.BYTES | 1007,
+
+    /**
+     * Tag::CERTIFICATE_NOT_BEFORE the beginning of the validity of the certificate in UNIX epoch
+     * time in milliseconds.  This value is used when generating attestation or self signed
+     * certificates.  ErrorCode::MISSING_NOT_BEFORE must be returned if this tag is not provided if
+     * this tag is not provided to generateKey or importKey.
+     */
+    CERTIFICATE_NOT_BEFORE = TagType.DATE | 1008,
+
+    /**
+     * Tag::CERTIFICATE_NOT_AFTER the end of the validity of the certificate in UNIX epoch time in
+     * milliseconds.  This value is used when generating attestation or self signed certificates.
+     * ErrorCode::MISSING_NOT_AFTER must be returned if this tag is not provided to generateKey or
+     * importKey.
+     */
+    CERTIFICATE_NOT_AFTER = TagType.DATE | 1009,
+
+    /**
+     * Tag::MAX_BOOT_LEVEL specifies a maximum boot level at which a key should function.
+     *
+     * Over the course of the init process, the boot level will be raised to
+     * monotonically increasing integer values. Implementations MUST NOT allow the key
+     * to be used once the boot level advances beyond the value of this tag.
+     *
+     * Cannot be hardware enforced in this version.
+     */
+    MAX_BOOT_LEVEL = TagType.UINT | 1010,
+}
diff --git a/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
new file mode 100644
index 0000000..d46e504
--- /dev/null
+++ b/security/keymint/aidl/android/hardware/security/keymint/TagType.aidl
@@ -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.
+ */
+
+package android.hardware.security.keymint;
+
+/**
+ * TagType classifies Tags in Tag.aidl into various groups of data.
+ * @hide
+ */
+@VintfStability
+@Backing(type="int")
+enum TagType {
+    /** Invalid type, used to designate a tag as uninitialized. */
+    INVALID = 0 << 28,
+    /** Enumeration value. */
+    ENUM = 1 << 28,
+    /** Repeatable enumeration value. */
+    ENUM_REP = 2 << 28,
+    /** 32-bit unsigned integer. */
+    UINT = 3 << 28,
+    /** Repeatable 32-bit unsigned integer. */
+    UINT_REP = 4 << 28,
+    /** 64-bit unsigned integer. */
+    ULONG = 5 << 28,
+    /** 64-bit unsigned integer representing a date and time, in milliseconds since 1 Jan 1970. */
+    DATE = 6 << 28,
+    /** Boolean.  If a tag with this type is present, the value is "true".  If absent, "false". */
+    BOOL = 7 << 28,
+    /**
+     * Byte string containing an arbitrary-length integer, in a two's-complement big-endian
+     * ordering.  The byte array contains the minimum number of bytes needed to represent the
+     * integer, including at least one sign bit (so zero encodes as the single byte 0x00.  This
+     * matches the encoding of both java.math.BigInteger.toByteArray() and contents octets for an
+     * ASN.1 INTEGER value (X.690 section 8.3).  Examples:
+     * - value 65536 encodes as 0x01 0x00 0x00
+     * - value 65535 encodes as 0x00 0xFF 0xFF
+     * - value   255 encodes as 0x00 0xFF
+     * - value     1 encodes as 0x01
+     * - value     0 encodes as 0x00
+     * - value    -1 encodes as 0xFF
+     * - value  -255 encodes as 0xFF 0x01
+     * - value  -256 encodes as 0xFF 0x00
+     */
+    BIGNUM = 8 << 28,
+    /** Byte string */
+    BYTES = 9 << 28,
+    /** Repeatable 64-bit unsigned integer */
+    ULONG_REP = 10 << 28,
+}
diff --git a/security/keymint/aidl/default/Android.bp b/security/keymint/aidl/default/Android.bp
new file mode 100644
index 0000000..230534c
--- /dev/null
+++ b/security/keymint/aidl/default/Android.bp
@@ -0,0 +1,52 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.security.keymint-service",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.security.keymint-service.rc"],
+    vintf_fragments: [
+        "android.hardware.security.keymint-service.xml",
+        "android.hardware.security.sharedsecret-service.xml",
+        "android.hardware.security.secureclock-service.xml",
+    ],
+    vendor: true,
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+        "libcppbor_external",
+        "libcrypto",
+        "libkeymaster_portable",
+        "libkeymint",
+        "liblog",
+        "libpuresoftkeymasterdevice",
+        "libutils",
+    ],
+    srcs: [
+        "service.cpp",
+    ],
+    required: [
+        "RemoteProvisioner",
+        "android.hardware.hardware_keystore.xml",
+    ],
+}
+
+prebuilt_etc {
+    name: "android.hardware.hardware_keystore.xml",
+    sub_dir: "permissions",
+    vendor: true,
+    src: "android.hardware.hardware_keystore.xml",
+}
diff --git a/security/keymint/aidl/default/android.hardware.hardware_keystore.xml b/security/keymint/aidl/default/android.hardware.hardware_keystore.xml
new file mode 100644
index 0000000..e5a9345
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.hardware_keystore.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright 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.
+-->
+<permissions>
+  <feature name="android.hardware.hardware_keystore" version="100" />
+</permissions>
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.rc b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
new file mode 100644
index 0000000..0c3a6e1
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.rc
@@ -0,0 +1,3 @@
+service vendor.keymint-default /vendor/bin/hw/android.hardware.security.keymint-service
+    class early_hal
+    user nobody
diff --git a/security/keymint/aidl/default/android.hardware.security.keymint-service.xml b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
new file mode 100644
index 0000000..4aa05ef
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.keymint-service.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IKeyMintDevice/default</fqname>
+    </hal>
+    <hal format="aidl">
+        <name>android.hardware.security.keymint</name>
+        <fqname>IRemotelyProvisionedComponent/default</fqname>
+    </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml b/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml
new file mode 100644
index 0000000..c0ff775
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.secureclock-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.secureclock</name>
+        <fqname>ISecureClock/default</fqname>
+    </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml b/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml
new file mode 100644
index 0000000..d37981f
--- /dev/null
+++ b/security/keymint/aidl/default/android.hardware.security.sharedsecret-service.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.sharedsecret</name>
+        <fqname>ISharedSecret/default</fqname>
+    </hal>
+</manifest>
diff --git a/security/keymint/aidl/default/service.cpp b/security/keymint/aidl/default/service.cpp
new file mode 100644
index 0000000..8092e34
--- /dev/null
+++ b/security/keymint/aidl/default/service.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "android.hardware.security.keymint-service"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <AndroidKeyMintDevice.h>
+#include <AndroidRemotelyProvisionedComponentDevice.h>
+#include <AndroidSecureClock.h>
+#include <AndroidSharedSecret.h>
+#include <keymaster/soft_keymaster_logger.h>
+
+using aidl::android::hardware::security::keymint::AndroidKeyMintDevice;
+using aidl::android::hardware::security::keymint::AndroidRemotelyProvisionedComponentDevice;
+using aidl::android::hardware::security::keymint::SecurityLevel;
+using aidl::android::hardware::security::secureclock::AndroidSecureClock;
+using aidl::android::hardware::security::sharedsecret::AndroidSharedSecret;
+
+template <typename T, class... Args>
+std::shared_ptr<T> addService(Args&&... args) {
+    std::shared_ptr<T> ser = ndk::SharedRefBase::make<T>(std::forward<Args>(args)...);
+    auto instanceName = std::string(T::descriptor) + "/default";
+    LOG(INFO) << "adding keymint service instance: " << instanceName;
+    binder_status_t status =
+            AServiceManager_addService(ser->asBinder().get(), instanceName.c_str());
+    CHECK(status == STATUS_OK);
+    return ser;
+}
+
+int main() {
+    // Zero threads seems like a useless pool, but below we'll join this thread to it, increasing
+    // the pool size to 1.
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    // Add Keymint Service
+    std::shared_ptr<AndroidKeyMintDevice> keyMint =
+            addService<AndroidKeyMintDevice>(SecurityLevel::SOFTWARE);
+    // Add Secure Clock Service
+    addService<AndroidSecureClock>(keyMint);
+    // Add Shared Secret Service
+    addService<AndroidSharedSecret>(keyMint);
+    // Add Remotely Provisioned Component Service
+    addService<AndroidRemotelyProvisionedComponentDevice>(keyMint);
+    ABinderProcess_joinThreadPool();
+    return EXIT_FAILURE;  // should not reach
+}
diff --git a/security/keymint/aidl/vts/functional/Android.bp b/security/keymint/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..386029f
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/Android.bp
@@ -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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_defaults {
+    name: "keymint_vts_defaults",
+    defaults: [
+        "use_libaidlvintf_gtest_helper_static",
+        "VtsHalTargetTestDefaults",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libjsoncpp",
+        "libkeymint",
+        "libkeymint_remote_prov_support",
+        "libkeymint_support",
+    ],
+}
+
+cc_test {
+    name: "VtsAidlKeyMintTargetTest",
+    defaults: [
+        "keymint_vts_defaults",
+    ],
+    srcs: [
+        "AttestKeyTest.cpp",
+        "DeviceUniqueAttestationTest.cpp",
+        "KeyMintTest.cpp",
+    ],
+    static_libs: [
+        "libkeymint_vts_test_utils",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test_library {
+    name: "libkeymint_vts_test_utils",
+    defaults: [
+        "keymint_vts_defaults",
+    ],
+    srcs: [
+        "KeyMintAidlTestBase.cpp",
+    ],
+    export_include_dirs: [
+        ".",
+    ],
+    static_libs: [
+        "libgmock_ndk",
+    ],
+}
+
+cc_test {
+    name: "VtsHalRemotelyProvisionedComponentTargetTest",
+    defaults: [
+        "keymint_vts_defaults",
+    ],
+    srcs: [
+        "VtsRemotelyProvisionedComponentTests.cpp",
+    ],
+    static_libs: [
+        "libgmock_ndk",
+        "libkeymaster_portable",
+        "libkeymint_vts_test_utils",
+        "libpuresoftkeymasterdevice",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/security/keymint/aidl/vts/functional/AndroidTest.xml b/security/keymint/aidl/vts/functional/AndroidTest.xml
new file mode 100644
index 0000000..de543f1
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/AndroidTest.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Runs VtsAidlKeyMintTargetTest.">
+    <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.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push"
+                value="VtsAidlKeyMintTargetTest->/data/local/tmp/VtsAidlKeyMintTargetTest" />
+    </target_preparer>
+
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="VtsAidlKeyMintTargetTest" />
+        <option name="native-test-timeout" value="900000"/>
+    </test>
+</configuration>
diff --git a/security/keymint/aidl/vts/functional/AttestKeyTest.cpp b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
new file mode 100644
index 0000000..a312723
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/AttestKeyTest.cpp
@@ -0,0 +1,785 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "keymint_1_attest_key_test"
+#include <cutils/log.h>
+
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+namespace aidl::android::hardware::security::keymint::test {
+
+namespace {
+
+bool IsSelfSigned(const vector<Certificate>& chain) {
+    if (chain.size() != 1) return false;
+    return ChainSignaturesAreValid(chain);
+}
+
+}  // namespace
+
+using AttestKeyTest = KeyMintAidlTestBase;
+
+/*
+ * AttestKeyTest.AllRsaSizes
+ *
+ * This test creates self signed RSA attestation keys of various sizes, and verify they can be
+ * used to sign other RSA and EC keys.
+ */
+TEST_P(AttestKeyTest, AllRsaSizes) {
+    for (auto size : ValidKeySizes(Algorithm::RSA)) {
+        /*
+         * Create attestation key.
+         */
+        AttestationKey attest_key;
+        vector<KeyCharacteristics> attest_key_characteristics;
+        vector<Certificate> attest_key_cert_chain;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(size, 65537)
+                                                     .AttestKey()
+                                                     .SetDefaultValidity(),
+                                             {} /* attestation signing key */, &attest_key.keyBlob,
+                                             &attest_key_characteristics, &attest_key_cert_chain));
+
+        ASSERT_GT(attest_key_cert_chain.size(), 0);
+        EXPECT_EQ(attest_key_cert_chain.size(), 1);
+        EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
+
+        /*
+         * Use attestation key to sign RSA signing key
+         */
+        attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attested_key_blob);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+        EXPECT_EQ(attested_key_cert_chain.size(), 2);
+
+        /*
+         * Use attestation key to sign RSA decryption key
+         */
+        attested_key_characteristics.resize(0);
+        attested_key_cert_chain.resize(0);
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaEncryptionKey(2048, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo2")
+                                      .AttestationApplicationId("bar2")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attested_key_blob);
+
+        hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo2", "bar2", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+        EXPECT_EQ(attested_key_cert_chain.size(), 2);
+
+        /*
+         * Use attestation key to sign EC key. Specify a CREATION_DATETIME for this one.
+         */
+        attested_key_characteristics.resize(0);
+        attested_key_cert_chain.resize(0);
+        uint64_t timestamp = 1619621648000;
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .Authorization(TAG_CREATION_DATETIME, timestamp)
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        // The returned key characteristics will include CREATION_DATETIME (checked below)
+        // in SecurityLevel::KEYSTORE; this will be stripped out in the CheckCharacteristics()
+        // call below, to match what getKeyCharacteristics() returns (which doesn't include
+        // any SecurityLevel::KEYSTORE characteristics).
+        CheckCharacteristics(attested_key_blob, attested_key_characteristics);
+
+        CheckedDeleteKey(&attested_key_blob);
+        CheckedDeleteKey(&attest_key.keyBlob);
+
+        hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+
+        // The client-specified CREATION_DATETIME should be in sw_enforced.
+        // Its presence will also trigger verify_attestation_record() to check that it
+        // is in the attestation extension with a matching value.
+        EXPECT_TRUE(sw_enforced.Contains(TAG_CREATION_DATETIME, timestamp))
+                << "expected CREATION_TIMESTAMP in sw_enforced:" << sw_enforced
+                << " not in hw_enforced:" << hw_enforced;
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Bail early if anything failed.
+        if (HasFailure()) return;
+    }
+}
+
+/*
+ * AttestKeyTest.RsaAttestedAttestKeys
+ *
+ * This test creates an RSA attestation key signed by factory keys, and varifies it can be
+ * used to sign other RSA and EC keys.
+ */
+TEST_P(AttestKeyTest, RsaAttestedAttestKeys) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    // An X.509 certificate serial number SHOULD be >0, but this is not policed. Check
+    // that a zero value doesn't cause problems.
+    uint64_t serial_int = 0;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    /*
+     * Create attestation key.
+     */
+    AttestationKey attest_key;
+    vector<KeyCharacteristics> attest_key_characteristics;
+    vector<Certificate> attest_key_cert_chain;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .AttestKey()
+                                  .AttestationChallenge(challenge)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .SetDefaultValidity(),
+                          {} /* attestation signing key */, &attest_key.keyBlob,
+                          &attest_key_characteristics, &attest_key_cert_chain));
+
+    EXPECT_GT(attest_key_cert_chain.size(), 1);
+    verify_subject_and_serial(attest_key_cert_chain[0], serial_int, subject, false);
+    EXPECT_TRUE(ChainSignaturesAreValid(attest_key_cert_chain));
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attest_key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attest_key_characteristics);
+    EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                          sw_enforced, hw_enforced, SecLevel(),
+                                          attest_key_cert_chain[0].encodedCertificate));
+
+    /*
+     * Use attestation key to sign RSA key
+     */
+    attest_key.issuerSubjectName = subject_der;
+    vector<uint8_t> attested_key_blob;
+    vector<KeyCharacteristics> attested_key_characteristics;
+    vector<Certificate> attested_key_cert_chain;
+
+    auto subject2 = "cert subject";
+    vector<uint8_t> subject_der2(make_name_from_str(subject2));
+
+    uint64_t serial_int2 = 255;
+    vector<uint8_t> serial_blob2(build_serial_blob(serial_int2));
+
+    EXPECT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .AttestationChallenge("foo")
+                                  .AttestationApplicationId("bar")
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob2)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der2)
+                                  .SetDefaultValidity(),
+                          attest_key, &attested_key_blob, &attested_key_characteristics,
+                          &attested_key_cert_chain));
+
+    CheckedDeleteKey(&attested_key_blob);
+    CheckedDeleteKey(&attest_key.keyBlob);
+
+    AuthorizationSet hw_enforced2 = HwEnforcedAuthorizations(attested_key_characteristics);
+    AuthorizationSet sw_enforced2 = SwEnforcedAuthorizations(attested_key_characteristics);
+    EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced2, hw_enforced2, SecLevel(),
+                                          attested_key_cert_chain[0].encodedCertificate));
+
+    // Attestation by itself is not valid (last entry is not self-signed).
+    EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+    // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+    attested_key_cert_chain.insert(attested_key_cert_chain.end(), attest_key_cert_chain.begin(),
+                                   attest_key_cert_chain.end());
+
+    EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+    EXPECT_GT(attested_key_cert_chain.size(), 2);
+    verify_subject_and_serial(attested_key_cert_chain[0], serial_int2, subject2, false);
+}
+
+/*
+ * AttestKeyTest.RsaAttestKeyChaining
+ *
+ * This test creates a chain of multiple RSA attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, RsaAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 7000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .AttestKey()
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                              &cert_chain_list[i]));
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+/*
+ * AttestKeyTest.EcAttestKeyChaining
+ *
+ * This test creates a chain of multiple Ec attest keys, each used to sign the next attest key,
+ * with the last attest key signed by the factory chain.
+ */
+TEST_P(AttestKeyTest, EcAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "Ec attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 800000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .AttestKey()
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .SetDefaultValidity(),
+                              attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                              &cert_chain_list[i]));
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+/*
+ * AttestKeyTest.AlternateAttestKeyChaining
+ *
+ * This test creates a chain of multiple attest keys, in the order Ec - RSA - Ec - RSA ....
+ * Each attest key is used to sign the next attest key, with the last attest key signed by
+ * the factory chain. This is to verify different algorithms of attest keys can
+ * cross sign each other and be chained together.
+ */
+TEST_P(AttestKeyTest, AlternateAttestKeyChaining) {
+    const int chain_size = 6;
+    vector<vector<uint8_t>> key_blob_list(chain_size);
+    vector<vector<Certificate>> cert_chain_list(chain_size);
+
+    for (int i = 0; i < chain_size; i++) {
+        string sub = "Alt attest key chaining ";
+        char index = '1' + i;
+        string subject = sub + index;
+        vector<uint8_t> subject_der(make_name_from_str(subject));
+
+        uint64_t serial_int = 90000000 + i;
+        vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+        vector<KeyCharacteristics> attested_key_characteristics;
+        AttestationKey attest_key;
+        optional<AttestationKey> attest_key_opt;
+
+        if (i > 0) {
+            attest_key.issuerSubjectName = make_name_from_str(sub + (char)(index - 1));
+            attest_key.keyBlob = key_blob_list[i - 1];
+            attest_key_opt = attest_key;
+        }
+
+        if ((i & 0x1) == 1) {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .EcdsaSigningKey(EcCurve::P_256)
+                                          .AttestKey()
+                                          .AttestationChallenge("foo")
+                                          .AttestationApplicationId("bar")
+                                          .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                          .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                                  &cert_chain_list[i]));
+        } else {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .RsaSigningKey(2048, 65537)
+                                          .AttestKey()
+                                          .AttestationChallenge("foo")
+                                          .AttestationApplicationId("bar")
+                                          .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                          .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  attest_key_opt, &key_blob_list[i], &attested_key_characteristics,
+                                  &cert_chain_list[i]));
+        }
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_list[i][0].encodedCertificate));
+
+        if (i > 0) {
+            /*
+             * The first key is attestated with factory chain, but all the rest of the keys are
+             * not supposed to be returned in attestation certificate chains.
+             */
+            EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_list[i]));
+
+            // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+            cert_chain_list[i].insert(cert_chain_list[i].end(),        //
+                                      cert_chain_list[i - 1].begin(),  //
+                                      cert_chain_list[i - 1].end());
+        }
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_list[i]));
+        EXPECT_GT(cert_chain_list[i].size(), i + 1);
+        verify_subject_and_serial(cert_chain_list[i][0], serial_int, subject, false);
+    }
+
+    for (int i = 0; i < chain_size; i++) {
+        CheckedDeleteKey(&key_blob_list[i]);
+    }
+}
+
+TEST_P(AttestKeyTest, MissingChallenge) {
+    for (auto size : ValidKeySizes(Algorithm::RSA)) {
+        /*
+         * Create attestation key.
+         */
+        AttestationKey attest_key;
+        vector<KeyCharacteristics> attest_key_characteristics;
+        vector<Certificate> attest_key_cert_chain;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(size, 65537)
+                                                     .AttestKey()
+                                                     .SetDefaultValidity(),
+                                             {} /* attestation signing key */, &attest_key.keyBlob,
+                                             &attest_key_characteristics, &attest_key_cert_chain));
+
+        EXPECT_EQ(attest_key_cert_chain.size(), 1);
+        EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on size " << size;
+
+        /*
+         * Use attestation key to sign RSA / ECDSA key but forget to provide a challenge
+         */
+        attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        EXPECT_EQ(ErrorCode::ATTESTATION_CHALLENGE_MISSING,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        EXPECT_EQ(ErrorCode::ATTESTATION_CHALLENGE_MISSING,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attest_key.keyBlob);
+    }
+}
+
+TEST_P(AttestKeyTest, AllEcCurves) {
+    for (auto curve : ValidCurves()) {
+        /*
+         * Create attestation key.
+         */
+        AttestationKey attest_key;
+        vector<KeyCharacteristics> attest_key_characteristics;
+        vector<Certificate> attest_key_cert_chain;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .AttestKey()
+                                                     .SetDefaultValidity(),
+                                             {} /* attestation signing key */, &attest_key.keyBlob,
+                                             &attest_key_characteristics, &attest_key_cert_chain));
+
+        ASSERT_GT(attest_key_cert_chain.size(), 0);
+        EXPECT_EQ(attest_key_cert_chain.size(), 1);
+        EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain)) << "Failed on curve " << curve;
+
+        /*
+         * Use attestation key to sign RSA key
+         */
+        attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(2048, 65537)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attested_key_blob);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        if (attest_key_cert_chain.size() > 0) {
+            attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        }
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        /*
+         * Use attestation key to sign EC key
+         */
+        EXPECT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .AttestationChallenge("foo")
+                                      .AttestationApplicationId("bar")
+                                      .SetDefaultValidity(),
+                              attest_key, &attested_key_blob, &attested_key_characteristics,
+                              &attested_key_cert_chain));
+
+        CheckedDeleteKey(&attested_key_blob);
+        CheckedDeleteKey(&attest_key.keyBlob);
+
+        hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced, SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Appending the attest_key chain to the attested_key_chain should yield a valid chain.
+        if (attest_key_cert_chain.size() > 0) {
+            attested_key_cert_chain.push_back(attest_key_cert_chain[0]);
+        }
+        EXPECT_TRUE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+        // Bail early if anything failed.
+        if (HasFailure()) return;
+    }
+}
+
+TEST_P(AttestKeyTest, AttestWithNonAttestKey) {
+    // Create non-attestation key.
+    AttestationKey non_attest_key;
+    vector<KeyCharacteristics> non_attest_key_characteristics;
+    vector<Certificate> non_attest_key_cert_chain;
+    ASSERT_EQ(
+            ErrorCode::OK,
+            GenerateKey(
+                    AuthorizationSetBuilder().EcdsaSigningKey(EcCurve::P_256).SetDefaultValidity(),
+                    {} /* attestation signing key */, &non_attest_key.keyBlob,
+                    &non_attest_key_characteristics, &non_attest_key_cert_chain));
+
+    ASSERT_GT(non_attest_key_cert_chain.size(), 0);
+    EXPECT_EQ(non_attest_key_cert_chain.size(), 1);
+    EXPECT_TRUE(IsSelfSigned(non_attest_key_cert_chain));
+
+    // Attempt to sign attestation with non-attest key.
+    vector<uint8_t> attested_key_blob;
+    vector<KeyCharacteristics> attested_key_characteristics;
+    vector<Certificate> attested_key_cert_chain;
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .EcdsaSigningKey(EcCurve::P_256)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .AttestationChallenge("foo")
+                                  .AttestationApplicationId("bar")
+                                  .SetDefaultValidity(),
+                          non_attest_key, &attested_key_blob, &attested_key_characteristics,
+                          &attested_key_cert_chain));
+}
+
+TEST_P(AttestKeyTest, EcdsaAttestationID) {
+    // Create attestation key.
+    AttestationKey attest_key;
+    vector<KeyCharacteristics> attest_key_characteristics;
+    vector<Certificate> attest_key_cert_chain;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .AttestKey()
+                                                 .SetDefaultValidity(),
+                                         {} /* attestation signing key */, &attest_key.keyBlob,
+                                         &attest_key_characteristics, &attest_key_cert_chain));
+    attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+    ASSERT_GT(attest_key_cert_chain.size(), 0);
+    EXPECT_EQ(attest_key_cert_chain.size(), 1);
+    EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain));
+
+    // Collection of valid attestation ID tags.
+    auto attestation_id_tags = AuthorizationSetBuilder();
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
+                      "ro.product.manufacturer");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
+
+    for (const KeyParameter& tag : attestation_id_tags) {
+        SCOPED_TRACE(testing::Message() << "+tag-" << tag);
+        // Use attestation key to sign an ECDSA key, but include an attestation ID field.
+        AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+                                                  .EcdsaSigningKey(EcCurve::P_256)
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .AttestationChallenge("challenge")
+                                                  .AttestationApplicationId("foo")
+                                                  .SetDefaultValidity();
+        builder.push_back(tag);
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        auto result = GenerateKey(builder, attest_key, &attested_key_blob,
+                                  &attested_key_characteristics, &attested_key_cert_chain);
+        if (result == ErrorCode::CANNOT_ATTEST_IDS) {
+            continue;
+        }
+
+        ASSERT_EQ(result, ErrorCode::OK);
+
+        CheckedDeleteKey(&attested_key_blob);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+
+        // The attested key characteristics will not contain APPLICATION_ID_* fields (their
+        // spec definitions all have "Must never appear in KeyCharacteristics"), but the
+        // attestation extension should contain them, so make sure the extra tag is added.
+        hw_enforced.push_back(tag);
+
+        EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
+                                              SecLevel(),
+                                              attested_key_cert_chain[0].encodedCertificate));
+    }
+    CheckedDeleteKey(&attest_key.keyBlob);
+}
+
+TEST_P(AttestKeyTest, EcdsaAttestationMismatchID) {
+    // Create attestation key.
+    AttestationKey attest_key;
+    vector<KeyCharacteristics> attest_key_characteristics;
+    vector<Certificate> attest_key_cert_chain;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .AttestKey()
+                                                 .SetDefaultValidity(),
+                                         {} /* attestation signing key */, &attest_key.keyBlob,
+                                         &attest_key_characteristics, &attest_key_cert_chain));
+    attest_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+    ASSERT_GT(attest_key_cert_chain.size(), 0);
+    EXPECT_EQ(attest_key_cert_chain.size(), 1);
+    EXPECT_TRUE(IsSelfSigned(attest_key_cert_chain));
+
+    // Collection of invalid attestation ID tags.
+    auto attestation_id_tags =
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand")
+                    .Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device")
+                    .Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product")
+                    .Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial")
+                    .Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei")
+                    .Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid")
+                    .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer")
+                    .Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model");
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    for (const KeyParameter& invalid_tag : attestation_id_tags) {
+        SCOPED_TRACE(testing::Message() << "+tag-" << invalid_tag);
+
+        // Use attestation key to sign an ECDSA key, but include an invalid
+        // attestation ID field.
+        AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+                                                  .EcdsaSigningKey(EcCurve::P_256)
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .AttestationChallenge("challenge")
+                                                  .AttestationApplicationId("foo")
+                                                  .SetDefaultValidity();
+        builder.push_back(invalid_tag);
+        vector<uint8_t> attested_key_blob;
+        vector<KeyCharacteristics> attested_key_characteristics;
+        vector<Certificate> attested_key_cert_chain;
+        auto result = GenerateKey(builder, attest_key, &attested_key_blob,
+                                  &attested_key_characteristics, &attested_key_cert_chain);
+
+        ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG)
+                << "result = " << result;
+    }
+    CheckedDeleteKey(&attest_key.keyBlob);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(AttestKeyTest);
+
+}  // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
new file mode 100644
index 0000000..d7abf07
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/DeviceUniqueAttestationTest.cpp
@@ -0,0 +1,331 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "keymint_1_attest_key_test"
+
+#include <cutils/log.h>
+#include <cutils/properties.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+namespace aidl::android::hardware::security::keymint::test {
+
+class DeviceUniqueAttestationTest : public KeyMintAidlTestBase {
+  protected:
+    void CheckUniqueAttestationResults(const vector<uint8_t>& key_blob,
+                                       const vector<KeyCharacteristics>& key_characteristics,
+                                       const AuthorizationSet& hw_enforced) {
+        ASSERT_GT(cert_chain_.size(), 0);
+
+        if (KeyMintAidlTestBase::dump_Attestations) {
+            std::cout << bin2hex(cert_chain_[0].encodedCertificate) << std::endl;
+        }
+
+        ASSERT_GT(key_blob.size(), 0U);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        // The device-unique attestation chain should contain exactly three certificates:
+        // * The leaf with the attestation extension.
+        // * An intermediate, signing the leaf using the device-unique key.
+        // * A self-signed root, signed using some authority's key, certifying
+        //   the device-unique key.
+        const size_t chain_length = cert_chain_.size();
+        ASSERT_TRUE(chain_length == 2 || chain_length == 3);
+        // TODO(b/191361618): Once StrongBox implementations use a correctly-issued
+        // certificate chain, do not skip issuers matching.
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_, /* strict_issuer_check= */ false));
+
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        EXPECT_TRUE(verify_attestation_record("challenge", "foo", sw_enforced, hw_enforced,
+                                              SecLevel(), cert_chain_[0].encodedCertificate));
+    }
+};
+
+/*
+ * DeviceUniqueAttestationTest.RsaNonStrongBoxUnimplemented
+ *
+ * Verifies that non strongbox implementations do not implement Rsa device unique
+ * attestation.
+ */
+TEST_P(DeviceUniqueAttestationTest, RsaNonStrongBoxUnimplemented) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    // Check RSA implementation
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .RsaSigningKey(2048, 65537)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                      .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                      .AttestationChallenge("challenge")
+                                      .AttestationApplicationId("foo")
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+                              &key_blob, &key_characteristics);
+
+    ASSERT_TRUE(result == ErrorCode::INVALID_ARGUMENT || result == ErrorCode::UNSUPPORTED_TAG);
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaNonStrongBoxUnimplemented
+ *
+ * Verifies that non strongbox implementations do not implement Ecdsa device unique
+ * attestation.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaNonStrongBoxUnimplemented) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    // Check Ecdsa implementation
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                      .AttestationChallenge("challenge")
+                                      .AttestationApplicationId("foo")
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+                              &key_blob, &key_characteristics);
+
+    ASSERT_TRUE(result == ErrorCode::INVALID_ARGUMENT || result == ErrorCode::UNSUPPORTED_TAG);
+}
+
+/*
+ * DeviceUniqueAttestationTest.RsaDeviceUniqueAttestation
+ *
+ * Verifies that strongbox implementations of Rsa implements device unique
+ * attestation correctly, if implemented.
+ */
+TEST_P(DeviceUniqueAttestationTest, RsaDeviceUniqueAttestation) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    int key_size = 2048;
+
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                      .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                      .AttestationChallenge("challenge")
+                                      .AttestationApplicationId("foo")
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+                              &key_blob, &key_characteristics);
+
+    // It is optional for Strong box to support DeviceUniqueAttestation.
+    if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+
+    ASSERT_EQ(ErrorCode::OK, result);
+
+    AuthorizationSetBuilder hw_enforced =
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                    .RsaSigningKey(2048, 65537)
+                    .Digest(Digest::SHA_2_256)
+                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                    .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                    .Authorization(TAG_OS_VERSION, os_version())
+                    .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+
+    // Any patchlevels attached to the key should also be present in the attestation extension.
+    AuthorizationSet auths;
+    for (const auto& entry : key_characteristics) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+    if (vendor_pl) {
+        hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
+    }
+    auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+    if (boot_pl) {
+        hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
+    }
+
+    CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestation
+ *
+ * Verifies that strongbox implementations of Rsa implements device unique
+ * attestation correctly, if implemented.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestation) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                      .AttestationChallenge("challenge")
+                                      .AttestationApplicationId("foo")
+                                      .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION),
+                              &key_blob, &key_characteristics);
+
+    // It is optional for Strong box to support DeviceUniqueAttestation.
+    if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+    ASSERT_EQ(ErrorCode::OK, result);
+
+    AuthorizationSetBuilder hw_enforced =
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                    .EcdsaSigningKey(EcCurve::P_256)
+                    .Digest(Digest::SHA_2_256)
+                    .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                    .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                    .Authorization(TAG_OS_VERSION, os_version())
+                    .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+    // Any patchlevels attached to the key should also be present in the attestation extension.
+    AuthorizationSet auths;
+    for (const auto& entry : key_characteristics) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+    if (vendor_pl) {
+        hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
+    }
+    auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+    if (boot_pl) {
+        hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
+    }
+
+    CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestationID
+ *
+ * Verifies that device unique attestation can include IDs that do match the
+ * local device.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationID) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    // Collection of valid attestation ID tags.
+    auto attestation_id_tags = AuthorizationSetBuilder();
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_BRAND, "ro.product.brand");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_DEVICE, "ro.product.device");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_PRODUCT, "ro.product.name");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_SERIAL, "ro.serial");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MANUFACTURER,
+                      "ro.product.manufacturer");
+    add_tag_from_prop(&attestation_id_tags, TAG_ATTESTATION_ID_MODEL, "ro.product.model");
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    for (const KeyParameter& tag : attestation_id_tags) {
+        SCOPED_TRACE(testing::Message() << "+tag-" << tag);
+        AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .EcdsaSigningKey(EcCurve::P_256)
+                                                  .Digest(Digest::SHA_2_256)
+                                                  .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                                  .AttestationChallenge("challenge")
+                                                  .AttestationApplicationId("foo")
+                                                  .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION);
+        builder.push_back(tag);
+        auto result = GenerateKey(builder, &key_blob, &key_characteristics);
+
+        // It is optional for Strong box to support DeviceUniqueAttestation.
+        if (result == ErrorCode::CANNOT_ATTEST_IDS) return;
+        ASSERT_EQ(ErrorCode::OK, result);
+
+        AuthorizationSetBuilder hw_enforced =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                        .EcdsaSigningKey(EcCurve::P_256)
+                        .Digest(Digest::SHA_2_256)
+                        .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION)
+                        .Authorization(TAG_ORIGIN, KeyOrigin::GENERATED)
+                        .Authorization(TAG_OS_VERSION, os_version())
+                        .Authorization(TAG_OS_PATCHLEVEL, os_patch_level());
+        // Expect the specified tag to be present in the attestation extension.
+        hw_enforced.push_back(tag);
+        // Any patchlevels attached to the key should also be present in the attestation extension.
+        AuthorizationSet auths;
+        for (const auto& entry : key_characteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+        if (vendor_pl) {
+            hw_enforced.Authorization(TAG_VENDOR_PATCHLEVEL, *vendor_pl);
+        }
+        auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+        if (boot_pl) {
+            hw_enforced.Authorization(TAG_BOOT_PATCHLEVEL, *boot_pl);
+        }
+        CheckUniqueAttestationResults(key_blob, key_characteristics, hw_enforced);
+    }
+}
+
+/*
+ * DeviceUniqueAttestationTest.EcdsaDeviceUniqueAttestationMismatchID
+ *
+ * Verifies that device unique attestation rejects attempts to attest to IDs that
+ * don't match the local device.
+ */
+TEST_P(DeviceUniqueAttestationTest, EcdsaDeviceUniqueAttestationMismatchID) {
+    if (SecLevel() != SecurityLevel::STRONGBOX) return;
+
+    // Collection of invalid attestation ID tags.
+    auto attestation_id_tags =
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_ATTESTATION_ID_BRAND, "bogus-brand")
+                    .Authorization(TAG_ATTESTATION_ID_DEVICE, "devious-device")
+                    .Authorization(TAG_ATTESTATION_ID_PRODUCT, "punctured-product")
+                    .Authorization(TAG_ATTESTATION_ID_SERIAL, "suspicious-serial")
+                    .Authorization(TAG_ATTESTATION_ID_IMEI, "invalid-imei")
+                    .Authorization(TAG_ATTESTATION_ID_MEID, "mismatching-meid")
+                    .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "malformed-manufacturer")
+                    .Authorization(TAG_ATTESTATION_ID_MODEL, "malicious-model");
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    for (const KeyParameter& invalid_tag : attestation_id_tags) {
+        SCOPED_TRACE(testing::Message() << "+tag-" << invalid_tag);
+        AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .EcdsaSigningKey(EcCurve::P_256)
+                                                  .Digest(Digest::SHA_2_256)
+                                                  .Authorization(TAG_INCLUDE_UNIQUE_ID)
+                                                  .AttestationChallenge("challenge")
+                                                  .AttestationApplicationId("foo")
+                                                  .Authorization(TAG_DEVICE_UNIQUE_ATTESTATION);
+        // Add the tag that doesn't match the local device's real ID.
+        builder.push_back(invalid_tag);
+        auto result = GenerateKey(builder, &key_blob, &key_characteristics);
+
+        ASSERT_TRUE(result == ErrorCode::CANNOT_ATTEST_IDS || result == ErrorCode::INVALID_TAG);
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(DeviceUniqueAttestationTest);
+
+}  // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
new file mode 100644
index 0000000..2032411
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.cpp
@@ -0,0 +1,1684 @@
+/*
+ * 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 "KeyMintAidlTestBase.h"
+
+#include <chrono>
+#include <unordered_set>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <cppbor_parse.h>
+#include <cutils/properties.h>
+#include <gmock/gmock.h>
+#include <openssl/mem.h>
+#include <remote_prov/remote_prov_utils.h>
+
+#include <keymaster/cppcose/cppcose.h>
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/keymint_utils.h>
+#include <keymint_support/openssl_utils.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using namespace cppcose;
+using namespace std::literals::chrono_literals;
+using std::endl;
+using std::optional;
+using std::unique_ptr;
+using ::testing::AssertionFailure;
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::ElementsAreArray;
+using ::testing::MatchesRegex;
+using ::testing::Not;
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set) {
+    if (set.size() == 0)
+        os << "(Empty)" << ::std::endl;
+    else {
+        os << "\n";
+        for (auto& entry : set) os << entry << ::std::endl;
+    }
+    return os;
+}
+
+namespace test {
+
+namespace {
+
+// Invalid value for a patchlevel (which is of form YYYYMMDD).
+const uint32_t kInvalidPatchlevel = 99998877;
+
+// Overhead for PKCS#1 v1.5 signature padding of undigested messages.  Digested messages have
+// additional overhead, for the digest algorithmIdentifier required by PKCS#1.
+const size_t kPkcs1UndigestedSignaturePaddingOverhead = 11;
+
+typedef KeyMintAidlTestBase::KeyData KeyData;
+// Predicate for testing basic characteristics validity in generation or import.
+bool KeyCharacteristicsBasicallyValid(SecurityLevel secLevel,
+                                      const vector<KeyCharacteristics>& key_characteristics) {
+    if (key_characteristics.empty()) return false;
+
+    std::unordered_set<SecurityLevel> levels_seen;
+    for (auto& entry : key_characteristics) {
+        if (entry.authorizations.empty()) return false;
+
+        // Just ignore the SecurityLevel::KEYSTORE as the KM won't do any enforcement on this.
+        if (entry.securityLevel == SecurityLevel::KEYSTORE) continue;
+
+        if (levels_seen.find(entry.securityLevel) != levels_seen.end()) return false;
+        levels_seen.insert(entry.securityLevel);
+
+        // Generally, we should only have one entry, at the same security level as the KM
+        // instance.  There is an exception: StrongBox KM can have some authorizations that are
+        // enforced by the TEE.
+        bool isExpectedSecurityLevel = secLevel == entry.securityLevel ||
+                                       (secLevel == SecurityLevel::STRONGBOX &&
+                                        entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT);
+
+        if (!isExpectedSecurityLevel) return false;
+    }
+    return true;
+}
+
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+ASN1_OCTET_STRING* get_attestation_record(X509* certificate) {
+    ASN1_OBJECT_Ptr oid(OBJ_txt2obj(kAttestionRecordOid, 1 /* dotted string format */));
+    EXPECT_TRUE(!!oid.get());
+    if (!oid.get()) return nullptr;
+
+    int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+    EXPECT_NE(-1, location) << "Attestation extension not found in certificate";
+    if (location == -1) return nullptr;
+
+    X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+    EXPECT_TRUE(!!attest_rec_ext)
+            << "Found attestation extension but couldn't retrieve it?  Probably a BoringSSL bug.";
+    if (!attest_rec_ext) return nullptr;
+
+    ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+    EXPECT_TRUE(!!attest_rec) << "Attestation extension contained no data";
+    return attest_rec;
+}
+
+bool avb_verification_enabled() {
+    char value[PROPERTY_VALUE_MAX];
+    return property_get("ro.boot.vbmeta.device_state", value, "") != 0;
+}
+
+char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+                       '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+
+// Attestations don't contain everything in key authorization lists, so we need to filter the key
+// lists to produce the lists that we expect to match the attestations.
+auto kTagsToFilter = {
+        Tag::CREATION_DATETIME,
+        Tag::HARDWARE_TYPE,
+        Tag::INCLUDE_UNIQUE_ID,
+};
+
+AuthorizationSet filtered_tags(const AuthorizationSet& set) {
+    AuthorizationSet filtered;
+    std::remove_copy_if(
+            set.begin(), set.end(), std::back_inserter(filtered), [](const auto& entry) -> bool {
+                return std::find(kTagsToFilter.begin(), kTagsToFilter.end(), entry.tag) !=
+                       kTagsToFilter.end();
+            });
+    return filtered;
+}
+
+// Remove any SecurityLevel::KEYSTORE entries from a list of key characteristics.
+void strip_keystore_tags(vector<KeyCharacteristics>* characteristics) {
+    characteristics->erase(std::remove_if(characteristics->begin(), characteristics->end(),
+                                          [](const auto& entry) {
+                                              return entry.securityLevel == SecurityLevel::KEYSTORE;
+                                          }),
+                           characteristics->end());
+}
+
+string x509NameToStr(X509_NAME* name) {
+    char* s = X509_NAME_oneline(name, nullptr, 0);
+    string retval(s);
+    OPENSSL_free(s);
+    return retval;
+}
+
+}  // namespace
+
+bool KeyMintAidlTestBase::arm_deleteAllKeys = false;
+bool KeyMintAidlTestBase::dump_Attestations = false;
+
+uint32_t KeyMintAidlTestBase::boot_patch_level(
+        const vector<KeyCharacteristics>& key_characteristics) {
+    // The boot patchlevel is not available as a property, but should be present
+    // in the key characteristics of any created key.
+    AuthorizationSet allAuths;
+    for (auto& entry : key_characteristics) {
+        allAuths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    auto patchlevel = allAuths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+    if (patchlevel.has_value()) {
+        return patchlevel.value();
+    } else {
+        // No boot patchlevel is available. Return a value that won't match anything
+        // and so will trigger test failures.
+        return kInvalidPatchlevel;
+    }
+}
+
+uint32_t KeyMintAidlTestBase::boot_patch_level() {
+    return boot_patch_level(key_characteristics_);
+}
+
+ErrorCode KeyMintAidlTestBase::GetReturnErrorCode(const Status& result) {
+    if (result.isOk()) return ErrorCode::OK;
+
+    if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+        return static_cast<ErrorCode>(result.getServiceSpecificError());
+    }
+
+    return ErrorCode::UNKNOWN_ERROR;
+}
+
+void KeyMintAidlTestBase::InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint) {
+    ASSERT_NE(keyMint, nullptr);
+    keymint_ = std::move(keyMint);
+
+    KeyMintHardwareInfo info;
+    ASSERT_TRUE(keymint_->getHardwareInfo(&info).isOk());
+
+    securityLevel_ = info.securityLevel;
+    name_.assign(info.keyMintName.begin(), info.keyMintName.end());
+    author_.assign(info.keyMintAuthorName.begin(), info.keyMintAuthorName.end());
+    timestamp_token_required_ = info.timestampTokenRequired;
+
+    os_version_ = getOsVersion();
+    os_patch_level_ = getOsPatchlevel();
+    vendor_patch_level_ = getVendorPatchlevel();
+}
+
+void KeyMintAidlTestBase::SetUp() {
+    if (AServiceManager_isDeclared(GetParam().c_str())) {
+        ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+        InitializeKeyMint(IKeyMintDevice::fromBinder(binder));
+    } else {
+        InitializeKeyMint(nullptr);
+    }
+}
+
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+                                           const optional<AttestationKey>& attest_key,
+                                           vector<uint8_t>* key_blob,
+                                           vector<KeyCharacteristics>* key_characteristics,
+                                           vector<Certificate>* cert_chain) {
+    EXPECT_NE(key_blob, nullptr) << "Key blob pointer must not be null.  Test bug";
+    EXPECT_NE(key_characteristics, nullptr)
+            << "Previous characteristics not deleted before generating key.  Test bug.";
+
+    KeyCreationResult creationResult;
+    Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
+    if (result.isOk()) {
+        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics);
+        EXPECT_GT(creationResult.keyBlob.size(), 0);
+        *key_blob = std::move(creationResult.keyBlob);
+        *key_characteristics = std::move(creationResult.keyCharacteristics);
+        *cert_chain = std::move(creationResult.certificateChain);
+
+        auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
+        EXPECT_TRUE(algorithm);
+        if (algorithm &&
+            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+            EXPECT_GE(cert_chain->size(), 1);
+            if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) {
+                if (attest_key) {
+                    EXPECT_EQ(cert_chain->size(), 1);
+                } else {
+                    EXPECT_GT(cert_chain->size(), 1);
+                }
+            }
+        } else {
+            // For symmetric keys there should be no certificates.
+            EXPECT_EQ(cert_chain->size(), 0);
+        }
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::GenerateKey(const AuthorizationSet& key_desc,
+                                           const optional<AttestationKey>& attest_key) {
+    return GenerateKey(key_desc, attest_key, &key_blob_, &key_characteristics_, &cert_chain_);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                                         const string& key_material, vector<uint8_t>* key_blob,
+                                         vector<KeyCharacteristics>* key_characteristics) {
+    Status result;
+
+    cert_chain_.clear();
+    key_characteristics->clear();
+    key_blob->clear();
+
+    KeyCreationResult creationResult;
+    result = keymint_->importKey(key_desc.vector_data(), format,
+                                 vector<uint8_t>(key_material.begin(), key_material.end()),
+                                 {} /* attestationSigningKeyBlob */, &creationResult);
+
+    if (result.isOk()) {
+        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics);
+        EXPECT_GT(creationResult.keyBlob.size(), 0);
+
+        *key_blob = std::move(creationResult.keyBlob);
+        *key_characteristics = std::move(creationResult.keyCharacteristics);
+        cert_chain_ = std::move(creationResult.certificateChain);
+
+        auto algorithm = key_desc.GetTagValue(TAG_ALGORITHM);
+        EXPECT_TRUE(algorithm);
+        if (algorithm &&
+            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+            EXPECT_GE(cert_chain_.size(), 1);
+            if (key_desc.Contains(TAG_ATTESTATION_CHALLENGE)) EXPECT_GT(cert_chain_.size(), 1);
+        } else {
+            // For symmetric keys there should be no certificates.
+            EXPECT_EQ(cert_chain_.size(), 0);
+        }
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                                         const string& key_material) {
+    return ImportKey(key_desc, format, key_material, &key_blob_, &key_characteristics_);
+}
+
+ErrorCode KeyMintAidlTestBase::ImportWrappedKey(string wrapped_key, string wrapping_key,
+                                                const AuthorizationSet& wrapping_key_desc,
+                                                string masking_key,
+                                                const AuthorizationSet& unwrapping_params,
+                                                int64_t password_sid, int64_t biometric_sid) {
+    EXPECT_EQ(ErrorCode::OK, ImportKey(wrapping_key_desc, KeyFormat::PKCS8, wrapping_key));
+
+    key_characteristics_.clear();
+
+    KeyCreationResult creationResult;
+    Status result = keymint_->importWrappedKey(
+            vector<uint8_t>(wrapped_key.begin(), wrapped_key.end()), key_blob_,
+            vector<uint8_t>(masking_key.begin(), masking_key.end()),
+            unwrapping_params.vector_data(), password_sid, biometric_sid, &creationResult);
+
+    if (result.isOk()) {
+        EXPECT_PRED2(KeyCharacteristicsBasicallyValid, SecLevel(),
+                     creationResult.keyCharacteristics);
+        EXPECT_GT(creationResult.keyBlob.size(), 0);
+
+        key_blob_ = std::move(creationResult.keyBlob);
+        key_characteristics_ = std::move(creationResult.keyCharacteristics);
+        cert_chain_ = std::move(creationResult.certificateChain);
+
+        AuthorizationSet allAuths;
+        for (auto& entry : key_characteristics_) {
+            allAuths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        auto algorithm = allAuths.GetTagValue(TAG_ALGORITHM);
+        EXPECT_TRUE(algorithm);
+        if (algorithm &&
+            (algorithm.value() == Algorithm::RSA || algorithm.value() == Algorithm::EC)) {
+            EXPECT_GE(cert_chain_.size(), 1);
+        } else {
+            // For symmetric keys there should be no certificates.
+            EXPECT_EQ(cert_chain_.size(), 0);
+        }
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::GetCharacteristics(const vector<uint8_t>& key_blob,
+                                                  const vector<uint8_t>& app_id,
+                                                  const vector<uint8_t>& app_data,
+                                                  vector<KeyCharacteristics>* key_characteristics) {
+    Status result =
+            keymint_->getKeyCharacteristics(key_blob, app_id, app_data, key_characteristics);
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::GetCharacteristics(const vector<uint8_t>& key_blob,
+                                                  vector<KeyCharacteristics>* key_characteristics) {
+    vector<uint8_t> empty_app_id, empty_app_data;
+    return GetCharacteristics(key_blob, empty_app_id, empty_app_data, key_characteristics);
+}
+
+void KeyMintAidlTestBase::CheckCharacteristics(
+        const vector<uint8_t>& key_blob,
+        const vector<KeyCharacteristics>& generate_characteristics) {
+    // Any key characteristics that were in SecurityLevel::KEYSTORE when returned from
+    // generateKey() should be excluded, as KeyMint will have no record of them.
+    // This applies to CREATION_DATETIME in particular.
+    vector<KeyCharacteristics> expected_characteristics(generate_characteristics);
+    strip_keystore_tags(&expected_characteristics);
+
+    vector<KeyCharacteristics> retrieved;
+    ASSERT_EQ(ErrorCode::OK, GetCharacteristics(key_blob, &retrieved));
+    EXPECT_EQ(expected_characteristics, retrieved);
+}
+
+void KeyMintAidlTestBase::CheckAppIdCharacteristics(
+        const vector<uint8_t>& key_blob, std::string_view app_id_string,
+        std::string_view app_data_string,
+        const vector<KeyCharacteristics>& generate_characteristics) {
+    // Exclude any SecurityLevel::KEYSTORE characteristics for comparisons.
+    vector<KeyCharacteristics> expected_characteristics(generate_characteristics);
+    strip_keystore_tags(&expected_characteristics);
+
+    vector<uint8_t> app_id(app_id_string.begin(), app_id_string.end());
+    vector<uint8_t> app_data(app_data_string.begin(), app_data_string.end());
+    vector<KeyCharacteristics> retrieved;
+    ASSERT_EQ(ErrorCode::OK, GetCharacteristics(key_blob, app_id, app_data, &retrieved));
+    EXPECT_EQ(expected_characteristics, retrieved);
+
+    // Check that key characteristics can't be retrieved if the app ID or app data is missing.
+    vector<uint8_t> empty;
+    vector<KeyCharacteristics> not_retrieved;
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              GetCharacteristics(key_blob, empty, app_data, &not_retrieved));
+    EXPECT_EQ(not_retrieved.size(), 0);
+
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              GetCharacteristics(key_blob, app_id, empty, &not_retrieved));
+    EXPECT_EQ(not_retrieved.size(), 0);
+
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              GetCharacteristics(key_blob, empty, empty, &not_retrieved));
+    EXPECT_EQ(not_retrieved.size(), 0);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
+    Status result = keymint_->deleteKey(*key_blob);
+    if (!keep_key_blob) {
+        *key_blob = vector<uint8_t>();
+    }
+
+    EXPECT_TRUE(result.isOk()) << result.getServiceSpecificError() << endl;
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteKey(bool keep_key_blob) {
+    return DeleteKey(&key_blob_, keep_key_blob);
+}
+
+ErrorCode KeyMintAidlTestBase::DeleteAllKeys() {
+    Status result = keymint_->deleteAllKeys();
+    EXPECT_TRUE(result.isOk()) << result.getServiceSpecificError() << endl;
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::DestroyAttestationIds() {
+    Status result = keymint_->destroyAttestationIds();
+    return GetReturnErrorCode(result);
+}
+
+void KeyMintAidlTestBase::CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob) {
+    ErrorCode result = DeleteKey(key_blob, keep_key_blob);
+    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED) << result << endl;
+}
+
+void KeyMintAidlTestBase::CheckedDeleteKey() {
+    CheckedDeleteKey(&key_blob_);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                                     const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params,
+                                     std::shared_ptr<IKeyMintOperation>& op) {
+    SCOPED_TRACE("Begin");
+    Status result;
+    BeginResult out;
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
+
+    if (result.isOk()) {
+        *out_params = out.params;
+        challenge_ = out.challenge;
+        op = out.operation;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                                     const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params) {
+    SCOPED_TRACE("Begin");
+    Status result;
+    BeginResult out;
+
+    result = keymint_->begin(purpose, key_blob, in_params.vector_data(), std::nullopt, &out);
+
+    if (result.isOk()) {
+        *out_params = out.params;
+        challenge_ = out.challenge;
+        op_ = out.operation;
+    }
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+                                     AuthorizationSet* out_params) {
+    SCOPED_TRACE("Begin");
+    EXPECT_EQ(nullptr, op_);
+    return Begin(purpose, key_blob_, in_params, out_params);
+}
+
+ErrorCode KeyMintAidlTestBase::Begin(KeyPurpose purpose, const AuthorizationSet& in_params) {
+    SCOPED_TRACE("Begin");
+    AuthorizationSet out_params;
+    ErrorCode result = Begin(purpose, in_params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UpdateAad(const string& input) {
+    return GetReturnErrorCode(op_->updateAad(vector<uint8_t>(input.begin(), input.end()),
+                                             {} /* hardwareAuthToken */,
+                                             {} /* verificationToken */));
+}
+
+ErrorCode KeyMintAidlTestBase::Update(const string& input, string* output) {
+    SCOPED_TRACE("Update");
+
+    Status result;
+    if (!output) return ErrorCode::UNEXPECTED_NULL_POINTER;
+
+    std::vector<uint8_t> o_put;
+    result = op_->update(vector<uint8_t>(input.begin(), input.end()), {}, {}, &o_put);
+
+    if (result.isOk()) output->append(o_put.begin(), o_put.end());
+
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Finish(const string& input, const string& signature,
+                                      string* output) {
+    SCOPED_TRACE("Finish");
+    Status result;
+
+    EXPECT_NE(op_, nullptr);
+    if (!op_) return ErrorCode::UNEXPECTED_NULL_POINTER;
+
+    vector<uint8_t> oPut;
+    result = op_->finish(vector<uint8_t>(input.begin(), input.end()),
+                         vector<uint8_t>(signature.begin(), signature.end()), {} /* authToken */,
+                         {} /* timestampToken */, {} /* confirmationToken */, &oPut);
+
+    if (result.isOk()) output->append(oPut.begin(), oPut.end());
+
+    op_ = {};
+    return GetReturnErrorCode(result);
+}
+
+ErrorCode KeyMintAidlTestBase::Abort(const std::shared_ptr<IKeyMintOperation>& op) {
+    SCOPED_TRACE("Abort");
+
+    EXPECT_NE(op, nullptr);
+    if (!op) return ErrorCode::UNEXPECTED_NULL_POINTER;
+
+    Status retval = op->abort();
+    EXPECT_TRUE(retval.isOk());
+    return static_cast<ErrorCode>(retval.getServiceSpecificError());
+}
+
+ErrorCode KeyMintAidlTestBase::Abort() {
+    SCOPED_TRACE("Abort");
+
+    EXPECT_NE(op_, nullptr);
+    if (!op_) return ErrorCode::UNEXPECTED_NULL_POINTER;
+
+    Status retval = op_->abort();
+    return static_cast<ErrorCode>(retval.getServiceSpecificError());
+}
+
+void KeyMintAidlTestBase::AbortIfNeeded() {
+    SCOPED_TRACE("AbortIfNeeded");
+    if (op_) {
+        EXPECT_EQ(ErrorCode::OK, Abort());
+        op_.reset();
+    }
+}
+
+auto KeyMintAidlTestBase::ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+                                         const string& message, const AuthorizationSet& in_params)
+        -> std::tuple<ErrorCode, string> {
+    AuthorizationSet begin_out_params;
+    ErrorCode result = Begin(operation, key_blob, in_params, &begin_out_params);
+    if (result != ErrorCode::OK) return {result, {}};
+
+    string output;
+    return {Finish(message, &output), output};
+}
+
+string KeyMintAidlTestBase::ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+                                           const string& message, const AuthorizationSet& in_params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("ProcessMessage");
+    AuthorizationSet begin_out_params;
+    ErrorCode result = Begin(operation, key_blob, in_params, out_params);
+    EXPECT_EQ(ErrorCode::OK, result);
+    if (result != ErrorCode::OK) {
+        return "";
+    }
+
+    string output;
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &output));
+    return output;
+}
+
+string KeyMintAidlTestBase::SignMessage(const vector<uint8_t>& key_blob, const string& message,
+                                        const AuthorizationSet& params) {
+    SCOPED_TRACE("SignMessage");
+    AuthorizationSet out_params;
+    string signature = ProcessMessage(key_blob, KeyPurpose::SIGN, message, params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return signature;
+}
+
+string KeyMintAidlTestBase::SignMessage(const string& message, const AuthorizationSet& params) {
+    SCOPED_TRACE("SignMessage");
+    return SignMessage(key_blob_, message, params);
+}
+
+string KeyMintAidlTestBase::MacMessage(const string& message, Digest digest, size_t mac_length) {
+    SCOPED_TRACE("MacMessage");
+    return SignMessage(
+            key_blob_, message,
+            AuthorizationSetBuilder().Digest(digest).Authorization(TAG_MAC_LENGTH, mac_length));
+}
+
+void KeyMintAidlTestBase::CheckHmacTestVector(const string& key, const string& message,
+                                              Digest digest, const string& expected_mac) {
+    SCOPED_TRACE("CheckHmacTestVector");
+    ASSERT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .HmacKey(key.size() * 8)
+                                .Authorization(TAG_MIN_MAC_LENGTH, expected_mac.size() * 8)
+                                .Digest(digest),
+                        KeyFormat::RAW, key));
+    string signature = MacMessage(message, digest, expected_mac.size() * 8);
+    EXPECT_EQ(expected_mac, signature)
+            << "Test vector didn't match for key of size " << key.size() << " message of size "
+            << message.size() << " and digest " << digest;
+    CheckedDeleteKey();
+}
+
+void KeyMintAidlTestBase::CheckAesCtrTestVector(const string& key, const string& nonce,
+                                                const string& message,
+                                                const string& expected_ciphertext) {
+    SCOPED_TRACE("CheckAesCtrTestVector");
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .AesEncryptionKey(key.size() * 8)
+                                               .BlockMode(BlockMode::CTR)
+                                               .Authorization(TAG_CALLER_NONCE)
+                                               .Padding(PaddingMode::NONE),
+                                       KeyFormat::RAW, key));
+
+    auto params = AuthorizationSetBuilder()
+                          .Authorization(TAG_NONCE, nonce.data(), nonce.size())
+                          .BlockMode(BlockMode::CTR)
+                          .Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(key_blob_, message, params, &out_params);
+    EXPECT_EQ(expected_ciphertext, ciphertext);
+}
+
+void KeyMintAidlTestBase::CheckTripleDesTestVector(KeyPurpose purpose, BlockMode block_mode,
+                                                   PaddingMode padding_mode, const string& key,
+                                                   const string& iv, const string& input,
+                                                   const string& expected_output) {
+    auto authset = AuthorizationSetBuilder()
+                           .TripleDesEncryptionKey(key.size() * 7)
+                           .BlockMode(block_mode)
+                           .Authorization(TAG_NO_AUTH_REQUIRED)
+                           .Padding(padding_mode);
+    if (iv.size()) authset.Authorization(TAG_CALLER_NONCE);
+    ASSERT_EQ(ErrorCode::OK, ImportKey(authset, KeyFormat::RAW, key));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    auto begin_params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
+    if (iv.size()) begin_params.Authorization(TAG_NONCE, iv.data(), iv.size());
+    AuthorizationSet output_params;
+    string output = ProcessMessage(key_blob_, purpose, input, begin_params, &output_params);
+    EXPECT_EQ(expected_output, output);
+}
+
+void KeyMintAidlTestBase::VerifyMessage(const vector<uint8_t>& key_blob, const string& message,
+                                        const string& signature, const AuthorizationSet& params) {
+    SCOPED_TRACE("VerifyMessage");
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::VERIFY, key_blob, params, &begin_out_params));
+
+    string output;
+    EXPECT_EQ(ErrorCode::OK, Finish(message, signature, &output));
+    EXPECT_TRUE(output.empty());
+    op_ = {};
+}
+
+void KeyMintAidlTestBase::VerifyMessage(const string& message, const string& signature,
+                                        const AuthorizationSet& params) {
+    SCOPED_TRACE("VerifyMessage");
+    VerifyMessage(key_blob_, message, signature, params);
+}
+
+void KeyMintAidlTestBase::LocalVerifyMessage(const string& message, const string& signature,
+                                             const AuthorizationSet& params) {
+    SCOPED_TRACE("LocalVerifyMessage");
+
+    // Retrieve the public key from the leaf certificate.
+    ASSERT_GT(cert_chain_.size(), 0);
+    X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    ASSERT_TRUE(key_cert.get());
+    EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
+    ASSERT_TRUE(pub_key.get());
+
+    Digest digest = params.GetTagValue(TAG_DIGEST).value();
+    PaddingMode padding = PaddingMode::NONE;
+    auto tag = params.GetTagValue(TAG_PADDING);
+    if (tag.has_value()) {
+        padding = tag.value();
+    }
+
+    if (digest == Digest::NONE) {
+        switch (EVP_PKEY_id(pub_key.get())) {
+            case EVP_PKEY_EC: {
+                vector<uint8_t> data((EVP_PKEY_bits(pub_key.get()) + 7) / 8);
+                size_t data_size = std::min(data.size(), message.size());
+                memcpy(data.data(), message.data(), data_size);
+                EC_KEY_Ptr ecdsa(EVP_PKEY_get1_EC_KEY(pub_key.get()));
+                ASSERT_TRUE(ecdsa.get());
+                ASSERT_EQ(1,
+                          ECDSA_verify(0, reinterpret_cast<const uint8_t*>(data.data()), data_size,
+                                       reinterpret_cast<const uint8_t*>(signature.data()),
+                                       signature.size(), ecdsa.get()));
+                break;
+            }
+            case EVP_PKEY_RSA: {
+                vector<uint8_t> data(EVP_PKEY_size(pub_key.get()));
+                size_t data_size = std::min(data.size(), message.size());
+                memcpy(data.data(), message.data(), data_size);
+
+                RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));
+                ASSERT_TRUE(rsa.get());
+
+                size_t key_len = RSA_size(rsa.get());
+                int openssl_padding = RSA_NO_PADDING;
+                switch (padding) {
+                    case PaddingMode::NONE:
+                        ASSERT_TRUE(data_size <= key_len);
+                        ASSERT_EQ(key_len, signature.size());
+                        openssl_padding = RSA_NO_PADDING;
+                        break;
+                    case PaddingMode::RSA_PKCS1_1_5_SIGN:
+                        ASSERT_TRUE(data_size + kPkcs1UndigestedSignaturePaddingOverhead <=
+                                    key_len);
+                        openssl_padding = RSA_PKCS1_PADDING;
+                        break;
+                    default:
+                        ADD_FAILURE() << "Unsupported RSA padding mode " << padding;
+                }
+
+                vector<uint8_t> decrypted_data(key_len);
+                int bytes_decrypted = RSA_public_decrypt(
+                        signature.size(), reinterpret_cast<const uint8_t*>(signature.data()),
+                        decrypted_data.data(), rsa.get(), openssl_padding);
+                ASSERT_GE(bytes_decrypted, 0);
+
+                const uint8_t* compare_pos = decrypted_data.data();
+                size_t bytes_to_compare = bytes_decrypted;
+                uint8_t zero_check_result = 0;
+                if (padding == PaddingMode::NONE && data_size < bytes_to_compare) {
+                    // If the data is short, for "unpadded" signing we zero-pad to the left.  So
+                    // during verification we should have zeros on the left of the decrypted data.
+                    // Do a constant-time check.
+                    const uint8_t* zero_end = compare_pos + bytes_to_compare - data_size;
+                    while (compare_pos < zero_end) zero_check_result |= *compare_pos++;
+                    ASSERT_EQ(0, zero_check_result);
+                    bytes_to_compare = data_size;
+                }
+                ASSERT_EQ(0, memcmp(compare_pos, data.data(), bytes_to_compare));
+                break;
+            }
+            default:
+                ADD_FAILURE() << "Unknown public key type";
+        }
+    } else {
+        EVP_MD_CTX digest_ctx;
+        EVP_MD_CTX_init(&digest_ctx);
+        EVP_PKEY_CTX* pkey_ctx;
+        const EVP_MD* md = openssl_digest(digest);
+        ASSERT_NE(md, nullptr);
+        ASSERT_EQ(1, EVP_DigestVerifyInit(&digest_ctx, &pkey_ctx, md, nullptr, pub_key.get()));
+
+        if (padding == PaddingMode::RSA_PSS) {
+            EXPECT_GT(EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING), 0);
+            EXPECT_GT(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, EVP_MD_size(md)), 0);
+        }
+
+        ASSERT_EQ(1, EVP_DigestVerifyUpdate(&digest_ctx,
+                                            reinterpret_cast<const uint8_t*>(message.data()),
+                                            message.size()));
+        ASSERT_EQ(1, EVP_DigestVerifyFinal(&digest_ctx,
+                                           reinterpret_cast<const uint8_t*>(signature.data()),
+                                           signature.size()));
+        EVP_MD_CTX_cleanup(&digest_ctx);
+    }
+}
+
+string KeyMintAidlTestBase::LocalRsaEncryptMessage(const string& message,
+                                                   const AuthorizationSet& params) {
+    SCOPED_TRACE("LocalRsaEncryptMessage");
+
+    // Retrieve the public key from the leaf certificate.
+    if (cert_chain_.empty()) {
+        ADD_FAILURE() << "No public key available";
+        return "Failure";
+    }
+    X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+    EVP_PKEY_Ptr pub_key(X509_get_pubkey(key_cert.get()));
+    RSA_Ptr rsa(EVP_PKEY_get1_RSA(const_cast<EVP_PKEY*>(pub_key.get())));
+
+    // Retrieve relevant tags.
+    Digest digest = Digest::NONE;
+    Digest mgf_digest = Digest::NONE;
+    PaddingMode padding = PaddingMode::NONE;
+
+    auto digest_tag = params.GetTagValue(TAG_DIGEST);
+    if (digest_tag.has_value()) digest = digest_tag.value();
+    auto pad_tag = params.GetTagValue(TAG_PADDING);
+    if (pad_tag.has_value()) padding = pad_tag.value();
+    auto mgf_tag = params.GetTagValue(TAG_RSA_OAEP_MGF_DIGEST);
+    if (mgf_tag.has_value()) mgf_digest = mgf_tag.value();
+
+    const EVP_MD* md = openssl_digest(digest);
+    const EVP_MD* mgf_md = openssl_digest(mgf_digest);
+
+    // Set up encryption context.
+    EVP_PKEY_CTX_Ptr ctx(EVP_PKEY_CTX_new(pub_key.get(), /* engine= */ nullptr));
+    if (EVP_PKEY_encrypt_init(ctx.get()) <= 0) {
+        ADD_FAILURE() << "Encryption init failed: " << ERR_peek_last_error();
+        return "Failure";
+    }
+
+    int rc = -1;
+    switch (padding) {
+        case PaddingMode::NONE:
+            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_NO_PADDING);
+            break;
+        case PaddingMode::RSA_PKCS1_1_5_ENCRYPT:
+            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_PADDING);
+            break;
+        case PaddingMode::RSA_OAEP:
+            rc = EVP_PKEY_CTX_set_rsa_padding(ctx.get(), RSA_PKCS1_OAEP_PADDING);
+            break;
+        default:
+            break;
+    }
+    if (rc <= 0) {
+        ADD_FAILURE() << "Set padding failed: " << ERR_peek_last_error();
+        return "Failure";
+    }
+    if (padding == PaddingMode::RSA_OAEP) {
+        if (!EVP_PKEY_CTX_set_rsa_oaep_md(ctx.get(), md)) {
+            ADD_FAILURE() << "Set digest failed: " << ERR_peek_last_error();
+            return "Failure";
+        }
+        if (!EVP_PKEY_CTX_set_rsa_mgf1_md(ctx.get(), mgf_md)) {
+            ADD_FAILURE() << "Set MGF digest failed: " << ERR_peek_last_error();
+            return "Failure";
+        }
+    }
+
+    // Determine output size.
+    size_t outlen;
+    if (EVP_PKEY_encrypt(ctx.get(), nullptr /* out */, &outlen,
+                         reinterpret_cast<const uint8_t*>(message.data()), message.size()) <= 0) {
+        ADD_FAILURE() << "Determine output size failed: " << ERR_peek_last_error();
+        return "Failure";
+    }
+
+    // Left-zero-pad the input if necessary.
+    const uint8_t* to_encrypt = reinterpret_cast<const uint8_t*>(message.data());
+    size_t to_encrypt_len = message.size();
+
+    std::unique_ptr<string> zero_padded_message;
+    if (padding == PaddingMode::NONE && to_encrypt_len < outlen) {
+        zero_padded_message.reset(new string(outlen, '\0'));
+        memcpy(zero_padded_message->data() + (outlen - to_encrypt_len), message.data(),
+               message.size());
+        to_encrypt = reinterpret_cast<const uint8_t*>(zero_padded_message->data());
+        to_encrypt_len = outlen;
+    }
+
+    // Do the encryption.
+    string output(outlen, '\0');
+    if (EVP_PKEY_encrypt(ctx.get(), reinterpret_cast<uint8_t*>(output.data()), &outlen, to_encrypt,
+                         to_encrypt_len) <= 0) {
+        ADD_FAILURE() << "Encryption failed: " << ERR_peek_last_error();
+        return "Failure";
+    }
+    return output;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
+                                           const AuthorizationSet& in_params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("EncryptMessage");
+    return ProcessMessage(key_blob, KeyPurpose::ENCRYPT, message, in_params, out_params);
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, const AuthorizationSet& params,
+                                           AuthorizationSet* out_params) {
+    SCOPED_TRACE("EncryptMessage");
+    return EncryptMessage(key_blob_, message, params, out_params);
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, const AuthorizationSet& params) {
+    SCOPED_TRACE("EncryptMessage");
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_TRUE(out_params.empty()) << "Output params should be empty. Contained: " << out_params;
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_TRUE(out_params.empty()) << "Output params should be empty. Contained: " << out_params;
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, vector<uint8_t>* iv_out) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(1U, out_params.size());
+    auto ivVal = out_params.GetTagValue(TAG_NONCE);
+    EXPECT_TRUE(ivVal);
+    if (ivVal) *iv_out = *ivVal;
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, const vector<uint8_t>& iv_in) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_NONCE, iv_in);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, uint8_t mac_length_bits,
+                                           const vector<uint8_t>& iv_in) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_MAC_LENGTH, mac_length_bits)
+                          .Authorization(TAG_NONCE, iv_in);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::EncryptMessage(const string& message, BlockMode block_mode,
+                                           PaddingMode padding, uint8_t mac_length_bits) {
+    SCOPED_TRACE("EncryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding)
+                          .Authorization(TAG_MAC_LENGTH, mac_length_bits);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    return ciphertext;
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const vector<uint8_t>& key_blob,
+                                           const string& ciphertext,
+                                           const AuthorizationSet& params) {
+    SCOPED_TRACE("DecryptMessage");
+    AuthorizationSet out_params;
+    string plaintext =
+            ProcessMessage(key_blob, KeyPurpose::DECRYPT, ciphertext, params, &out_params);
+    EXPECT_TRUE(out_params.empty());
+    return plaintext;
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const string& ciphertext,
+                                           const AuthorizationSet& params) {
+    SCOPED_TRACE("DecryptMessage");
+    return DecryptMessage(key_blob_, ciphertext, params);
+}
+
+string KeyMintAidlTestBase::DecryptMessage(const string& ciphertext, BlockMode block_mode,
+                                           PaddingMode padding_mode, const vector<uint8_t>& iv) {
+    SCOPED_TRACE("DecryptMessage");
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(block_mode)
+                          .Padding(padding_mode)
+                          .Authorization(TAG_NONCE, iv);
+    return DecryptMessage(key_blob_, ciphertext, params);
+}
+
+std::pair<ErrorCode, vector<uint8_t>> KeyMintAidlTestBase::UpgradeKey(
+        const vector<uint8_t>& key_blob) {
+    std::pair<ErrorCode, vector<uint8_t>> retval;
+    vector<uint8_t> outKeyBlob;
+    Status result = keymint_->upgradeKey(key_blob, vector<KeyParameter>(), &outKeyBlob);
+    ErrorCode errorcode = GetReturnErrorCode(result);
+    retval = std::tie(errorcode, outKeyBlob);
+
+    return retval;
+}
+vector<uint32_t> KeyMintAidlTestBase::ValidKeySizes(Algorithm algorithm) {
+    switch (algorithm) {
+        case Algorithm::RSA:
+            switch (SecLevel()) {
+                case SecurityLevel::SOFTWARE:
+                case SecurityLevel::TRUSTED_ENVIRONMENT:
+                    return {2048, 3072, 4096};
+                case SecurityLevel::STRONGBOX:
+                    return {2048};
+                default:
+                    ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
+                    break;
+            }
+            break;
+        case Algorithm::EC:
+            ADD_FAILURE() << "EC keys must be specified by curve not size";
+            break;
+        case Algorithm::AES:
+            return {128, 256};
+        case Algorithm::TRIPLE_DES:
+            return {168};
+        case Algorithm::HMAC: {
+            vector<uint32_t> retval((512 - 64) / 8 + 1);
+            uint32_t size = 64 - 8;
+            std::generate(retval.begin(), retval.end(), [&]() { return (size += 8); });
+            return retval;
+        }
+        default:
+            ADD_FAILURE() << "Invalid Algorithm: " << algorithm;
+            return {};
+    }
+    ADD_FAILURE() << "Should be impossible to get here";
+    return {};
+}
+
+vector<uint32_t> KeyMintAidlTestBase::InvalidKeySizes(Algorithm algorithm) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        switch (algorithm) {
+            case Algorithm::RSA:
+                return {3072, 4096};
+            case Algorithm::EC:
+                return {224, 384, 521};
+            case Algorithm::AES:
+                return {192};
+            case Algorithm::TRIPLE_DES:
+                return {56};
+            default:
+                return {};
+        }
+    } else {
+        switch (algorithm) {
+            case Algorithm::TRIPLE_DES:
+                return {56};
+            default:
+                return {};
+        }
+    }
+    return {};
+}
+
+vector<BlockMode> KeyMintAidlTestBase::ValidBlockModes(Algorithm algorithm) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            return {
+                    BlockMode::CBC,
+                    BlockMode::CTR,
+                    BlockMode::ECB,
+                    BlockMode::GCM,
+            };
+        case Algorithm::TRIPLE_DES:
+            return {
+                    BlockMode::CBC,
+                    BlockMode::ECB,
+            };
+        default:
+            return {};
+    }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::ValidPaddingModes(Algorithm algorithm,
+                                                           BlockMode blockMode) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            switch (blockMode) {
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    return {PaddingMode::NONE, PaddingMode::PKCS7};
+                case BlockMode::CTR:
+                case BlockMode::GCM:
+                    return {PaddingMode::NONE};
+                default:
+                    return {};
+            };
+        case Algorithm::TRIPLE_DES:
+            switch (blockMode) {
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    return {PaddingMode::NONE, PaddingMode::PKCS7};
+                default:
+                    return {};
+            };
+        default:
+            return {};
+    }
+}
+
+vector<PaddingMode> KeyMintAidlTestBase::InvalidPaddingModes(Algorithm algorithm,
+                                                             BlockMode blockMode) {
+    switch (algorithm) {
+        case Algorithm::AES:
+            switch (blockMode) {
+                case BlockMode::CTR:
+                case BlockMode::GCM:
+                    return {PaddingMode::PKCS7};
+                default:
+                    return {};
+            };
+        default:
+            return {};
+    }
+}
+
+vector<EcCurve> KeyMintAidlTestBase::ValidCurves() {
+    if (securityLevel_ == SecurityLevel::STRONGBOX) {
+        return {EcCurve::P_256};
+    } else {
+        return {EcCurve::P_224, EcCurve::P_256, EcCurve::P_384, EcCurve::P_521};
+    }
+}
+
+vector<EcCurve> KeyMintAidlTestBase::InvalidCurves() {
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        return {EcCurve::P_224, EcCurve::P_384, EcCurve::P_521};
+    } else {
+        return {};
+    }
+}
+
+vector<Digest> KeyMintAidlTestBase::ValidDigests(bool withNone, bool withMD5) {
+    switch (SecLevel()) {
+        case SecurityLevel::SOFTWARE:
+        case SecurityLevel::TRUSTED_ENVIRONMENT:
+            if (withNone) {
+                if (withMD5)
+                    return {Digest::NONE,      Digest::MD5,       Digest::SHA1,
+                            Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+                            Digest::SHA_2_512};
+                else
+                    return {Digest::NONE,      Digest::SHA1,      Digest::SHA_2_224,
+                            Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512};
+            } else {
+                if (withMD5)
+                    return {Digest::MD5,       Digest::SHA1,      Digest::SHA_2_224,
+                            Digest::SHA_2_256, Digest::SHA_2_384, Digest::SHA_2_512};
+                else
+                    return {Digest::SHA1, Digest::SHA_2_224, Digest::SHA_2_256, Digest::SHA_2_384,
+                            Digest::SHA_2_512};
+            }
+            break;
+        case SecurityLevel::STRONGBOX:
+            if (withNone)
+                return {Digest::NONE, Digest::SHA_2_256};
+            else
+                return {Digest::SHA_2_256};
+            break;
+        default:
+            ADD_FAILURE() << "Invalid security level " << uint32_t(SecLevel());
+            break;
+    }
+    ADD_FAILURE() << "Should be impossible to get here";
+    return {};
+}
+
+static const vector<KeyParameter> kEmptyAuthList{};
+
+const vector<KeyParameter>& KeyMintAidlTestBase::SecLevelAuthorizations(
+        const vector<KeyCharacteristics>& key_characteristics) {
+    auto found = std::find_if(key_characteristics.begin(), key_characteristics.end(),
+                              [this](auto& entry) { return entry.securityLevel == SecLevel(); });
+    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
+}
+
+const vector<KeyParameter>& KeyMintAidlTestBase::SecLevelAuthorizations(
+        const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel) {
+    auto found = std::find_if(
+            key_characteristics.begin(), key_characteristics.end(),
+            [securityLevel](auto& entry) { return entry.securityLevel == securityLevel; });
+    return (found == key_characteristics.end()) ? kEmptyAuthList : found->authorizations;
+}
+
+ErrorCode KeyMintAidlTestBase::UseAesKey(const vector<uint8_t>& aesKeyBlob) {
+    auto [result, ciphertext] = ProcessMessage(
+            aesKeyBlob, KeyPurpose::ENCRYPT, "1234567890123456",
+            AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE));
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseHmacKey(const vector<uint8_t>& hmacKeyBlob) {
+    auto [result, mac] = ProcessMessage(
+            hmacKeyBlob, KeyPurpose::SIGN, "1234567890123456",
+            AuthorizationSetBuilder().Authorization(TAG_MAC_LENGTH, 128).Digest(Digest::SHA_2_256));
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseRsaKey(const vector<uint8_t>& rsaKeyBlob) {
+    std::string message(2048 / 8, 'a');
+    auto [result, signature] = ProcessMessage(
+            rsaKeyBlob, KeyPurpose::SIGN, message,
+            AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+    return result;
+}
+
+ErrorCode KeyMintAidlTestBase::UseEcdsaKey(const vector<uint8_t>& ecdsaKeyBlob) {
+    auto [result, signature] = ProcessMessage(ecdsaKeyBlob, KeyPurpose::SIGN, "a",
+                                              AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+    return result;
+}
+
+void verify_serial(X509* cert, const uint64_t expected_serial) {
+    BIGNUM_Ptr ser(BN_new());
+    EXPECT_TRUE(ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), ser.get()));
+
+    uint64_t serial;
+    EXPECT_TRUE(BN_get_u64(ser.get(), &serial));
+    EXPECT_EQ(serial, expected_serial);
+}
+
+// Please set self_signed to true for fake certificates or self signed
+// certificates
+void verify_subject(const X509* cert,       //
+                    const string& subject,  //
+                    bool self_signed) {
+    char* cert_issuer =  //
+            X509_NAME_oneline(X509_get_issuer_name(cert), nullptr, 0);
+
+    char* cert_subj = X509_NAME_oneline(X509_get_subject_name(cert), nullptr, 0);
+
+    string expected_subject("/CN=");
+    if (subject.empty()) {
+        expected_subject.append("Android Keystore Key");
+    } else {
+        expected_subject.append(subject);
+    }
+
+    EXPECT_STREQ(expected_subject.c_str(), cert_subj) << "Cert has wrong subject." << cert_subj;
+
+    if (self_signed) {
+        EXPECT_STREQ(cert_issuer, cert_subj)
+                << "Cert issuer and subject mismatch for self signed certificate.";
+    }
+
+    OPENSSL_free(cert_subj);
+    OPENSSL_free(cert_issuer);
+}
+
+vector<uint8_t> build_serial_blob(const uint64_t serial_int) {
+    BIGNUM_Ptr serial(BN_new());
+    EXPECT_TRUE(BN_set_u64(serial.get(), serial_int));
+
+    int len = BN_num_bytes(serial.get());
+    vector<uint8_t> serial_blob(len);
+    if (BN_bn2bin(serial.get(), serial_blob.data()) != len) {
+        return {};
+    }
+
+    if (serial_blob.empty() || serial_blob[0] & 0x80) {
+        // An empty blob is OpenSSL's encoding of the zero value; we need single zero byte.
+        // Top bit being set indicates a negative number in two's complement, but our input
+        // was positive.
+        // In either case, prepend a zero byte.
+        serial_blob.insert(serial_blob.begin(), 0x00);
+    }
+
+    return serial_blob;
+}
+
+void verify_subject_and_serial(const Certificate& certificate,  //
+                               const uint64_t expected_serial,  //
+                               const string& subject, bool self_signed) {
+    X509_Ptr cert(parse_cert_blob(certificate.encodedCertificate));
+    ASSERT_TRUE(!!cert.get());
+
+    verify_serial(cert.get(), expected_serial);
+    verify_subject(cert.get(), subject, self_signed);
+}
+
+bool verify_attestation_record(const string& challenge,                //
+                               const string& app_id,                   //
+                               AuthorizationSet expected_sw_enforced,  //
+                               AuthorizationSet expected_hw_enforced,  //
+                               SecurityLevel security_level,
+                               const vector<uint8_t>& attestation_cert) {
+    X509_Ptr cert(parse_cert_blob(attestation_cert));
+    EXPECT_TRUE(!!cert.get());
+    if (!cert.get()) return false;
+
+    ASN1_OCTET_STRING* attest_rec = get_attestation_record(cert.get());
+    EXPECT_TRUE(!!attest_rec);
+    if (!attest_rec) return false;
+
+    AuthorizationSet att_sw_enforced;
+    AuthorizationSet att_hw_enforced;
+    uint32_t att_attestation_version;
+    uint32_t att_keymint_version;
+    SecurityLevel att_attestation_security_level;
+    SecurityLevel att_keymint_security_level;
+    vector<uint8_t> att_challenge;
+    vector<uint8_t> att_unique_id;
+    vector<uint8_t> att_app_id;
+
+    auto error = parse_attestation_record(attest_rec->data,                 //
+                                          attest_rec->length,               //
+                                          &att_attestation_version,         //
+                                          &att_attestation_security_level,  //
+                                          &att_keymint_version,             //
+                                          &att_keymint_security_level,      //
+                                          &att_challenge,                   //
+                                          &att_sw_enforced,                 //
+                                          &att_hw_enforced,                 //
+                                          &att_unique_id);
+    EXPECT_EQ(ErrorCode::OK, error);
+    if (error != ErrorCode::OK) return false;
+
+    EXPECT_EQ(att_attestation_version, 100U);
+    vector<uint8_t> appId(app_id.begin(), app_id.end());
+
+    // check challenge and app id only if we expects a non-fake certificate
+    if (challenge.length() > 0) {
+        EXPECT_EQ(challenge.length(), att_challenge.size());
+        EXPECT_EQ(0, memcmp(challenge.data(), att_challenge.data(), challenge.length()));
+
+        expected_sw_enforced.push_back(TAG_ATTESTATION_APPLICATION_ID, appId);
+    }
+
+    EXPECT_EQ(att_keymint_version, 100U);
+    EXPECT_EQ(security_level, att_keymint_security_level);
+    EXPECT_EQ(security_level, att_attestation_security_level);
+
+
+    char property_value[PROPERTY_VALUE_MAX] = {};
+    // TODO(b/136282179): When running under VTS-on-GSI the TEE-backed
+    // keymint implementation will report YYYYMM dates instead of YYYYMMDD
+    // for the BOOT_PATCH_LEVEL.
+    if (avb_verification_enabled()) {
+        for (int i = 0; i < att_hw_enforced.size(); i++) {
+            if (att_hw_enforced[i].tag == TAG_BOOT_PATCHLEVEL ||
+                att_hw_enforced[i].tag == TAG_VENDOR_PATCHLEVEL) {
+                std::string date =
+                        std::to_string(att_hw_enforced[i].value.get<KeyParameterValue::integer>());
+                // strptime seems to require delimiters, but the tag value will
+                // be YYYYMMDD
+                date.insert(6, "-");
+                date.insert(4, "-");
+                EXPECT_EQ(date.size(), 10);
+                struct tm time;
+                strptime(date.c_str(), "%Y-%m-%d", &time);
+
+                // Day of the month (0-31)
+                EXPECT_GE(time.tm_mday, 0);
+                EXPECT_LT(time.tm_mday, 32);
+                // Months since Jan (0-11)
+                EXPECT_GE(time.tm_mon, 0);
+                EXPECT_LT(time.tm_mon, 12);
+                // Years since 1900
+                EXPECT_GT(time.tm_year, 110);
+                EXPECT_LT(time.tm_year, 200);
+            }
+        }
+    }
+
+    // Check to make sure boolean values are properly encoded. Presence of a boolean tag
+    // indicates true. A provided boolean tag that can be pulled back out of the certificate
+    // indicates correct encoding. No need to check if it's in both lists, since the
+    // AuthorizationSet compare below will handle mismatches of tags.
+    if (security_level == SecurityLevel::SOFTWARE) {
+        EXPECT_TRUE(expected_sw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+    } else {
+        EXPECT_TRUE(expected_hw_enforced.Contains(TAG_NO_AUTH_REQUIRED));
+    }
+
+    if (att_hw_enforced.Contains(TAG_ALGORITHM, Algorithm::EC)) {
+        // For ECDSA keys, either an EC_CURVE or a KEY_SIZE can be specified, but one must be.
+        EXPECT_TRUE(att_hw_enforced.Contains(TAG_EC_CURVE) ||
+                    att_hw_enforced.Contains(TAG_KEY_SIZE));
+    }
+
+    // Test root of trust elements
+    vector<uint8_t> verified_boot_key;
+    VerifiedBoot verified_boot_state;
+    bool device_locked;
+    vector<uint8_t> verified_boot_hash;
+    error = parse_root_of_trust(attest_rec->data, attest_rec->length, &verified_boot_key,
+                                &verified_boot_state, &device_locked, &verified_boot_hash);
+    EXPECT_EQ(ErrorCode::OK, error);
+
+    if (avb_verification_enabled()) {
+        EXPECT_NE(property_get("ro.boot.vbmeta.digest", property_value, ""), 0);
+        string prop_string(property_value);
+        EXPECT_EQ(prop_string.size(), 64);
+        EXPECT_EQ(prop_string, bin2hex(verified_boot_hash));
+
+        EXPECT_NE(property_get("ro.boot.vbmeta.device_state", property_value, ""), 0);
+        if (!strcmp(property_value, "unlocked")) {
+            EXPECT_FALSE(device_locked);
+        } else {
+            EXPECT_TRUE(device_locked);
+        }
+
+        // Check that the device is locked if not debuggable, e.g., user build
+        // images in CTS. For VTS, debuggable images are used to allow adb root
+        // and the device is unlocked.
+        if (!property_get_bool("ro.debuggable", false)) {
+            EXPECT_TRUE(device_locked);
+        } else {
+            EXPECT_FALSE(device_locked);
+        }
+    }
+
+    // Verified boot key should be all 0's if the boot state is not verified or self signed
+    std::string empty_boot_key(32, '\0');
+    std::string verified_boot_key_str((const char*)verified_boot_key.data(),
+                                      verified_boot_key.size());
+    EXPECT_NE(property_get("ro.boot.verifiedbootstate", property_value, ""), 0);
+    if (!strcmp(property_value, "green")) {
+        EXPECT_EQ(verified_boot_state, VerifiedBoot::VERIFIED);
+        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+                            verified_boot_key.size()));
+    } else if (!strcmp(property_value, "yellow")) {
+        EXPECT_EQ(verified_boot_state, VerifiedBoot::SELF_SIGNED);
+        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+                            verified_boot_key.size()));
+    } else if (!strcmp(property_value, "orange")) {
+        EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+        EXPECT_EQ(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+                            verified_boot_key.size()));
+    } else if (!strcmp(property_value, "red")) {
+        EXPECT_EQ(verified_boot_state, VerifiedBoot::FAILED);
+    } else {
+        EXPECT_EQ(verified_boot_state, VerifiedBoot::UNVERIFIED);
+        EXPECT_NE(0, memcmp(verified_boot_key.data(), empty_boot_key.data(),
+                            verified_boot_key.size()));
+    }
+
+    att_sw_enforced.Sort();
+    expected_sw_enforced.Sort();
+    EXPECT_EQ(filtered_tags(expected_sw_enforced), filtered_tags(att_sw_enforced));
+
+    att_hw_enforced.Sort();
+    expected_hw_enforced.Sort();
+    EXPECT_EQ(filtered_tags(expected_hw_enforced), filtered_tags(att_hw_enforced));
+
+    return true;
+}
+
+string bin2hex(const vector<uint8_t>& data) {
+    string retval;
+    retval.reserve(data.size() * 2 + 1);
+    for (uint8_t byte : data) {
+        retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
+        retval.push_back(nibble2hex[0x0F & byte]);
+    }
+    return retval;
+}
+
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+    AuthorizationSet authList;
+    for (auto& entry : key_characteristics) {
+        if (entry.securityLevel == SecurityLevel::STRONGBOX ||
+            entry.securityLevel == SecurityLevel::TRUSTED_ENVIRONMENT) {
+            authList.push_back(AuthorizationSet(entry.authorizations));
+        }
+    }
+    return authList;
+}
+
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics) {
+    AuthorizationSet authList;
+    for (auto& entry : key_characteristics) {
+        if (entry.securityLevel == SecurityLevel::SOFTWARE ||
+            entry.securityLevel == SecurityLevel::KEYSTORE) {
+            authList.push_back(AuthorizationSet(entry.authorizations));
+        }
+    }
+    return authList;
+}
+
+AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
+                                        bool strict_issuer_check) {
+    std::stringstream cert_data;
+
+    for (size_t i = 0; i < chain.size(); ++i) {
+        cert_data << bin2hex(chain[i].encodedCertificate) << std::endl;
+
+        X509_Ptr key_cert(parse_cert_blob(chain[i].encodedCertificate));
+        X509_Ptr signing_cert;
+        if (i < chain.size() - 1) {
+            signing_cert = parse_cert_blob(chain[i + 1].encodedCertificate);
+        } else {
+            signing_cert = parse_cert_blob(chain[i].encodedCertificate);
+        }
+        if (!key_cert.get() || !signing_cert.get()) return AssertionFailure() << cert_data.str();
+
+        EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+        if (!signing_pubkey.get()) return AssertionFailure() << cert_data.str();
+
+        if (!X509_verify(key_cert.get(), signing_pubkey.get())) {
+            return AssertionFailure()
+                   << "Verification of certificate " << i << " failed "
+                   << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL) << '\n'
+                   << cert_data.str();
+        }
+
+        string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
+        string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
+        if (cert_issuer != signer_subj && strict_issuer_check) {
+            return AssertionFailure() << "Cert " << i << " has wrong issuer.\n"
+                                      << " Signer subject is " << signer_subj
+                                      << " Issuer subject is " << cert_issuer << endl
+                                      << cert_data.str();
+        }
+    }
+
+    if (KeyMintAidlTestBase::dump_Attestations) std::cout << cert_data.str();
+    return AssertionSuccess();
+}
+
+X509_Ptr parse_cert_blob(const vector<uint8_t>& blob) {
+    const uint8_t* p = blob.data();
+    return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
+}
+
+vector<uint8_t> make_name_from_str(const string& name) {
+    X509_NAME_Ptr x509_name(X509_NAME_new());
+    EXPECT_TRUE(x509_name.get() != nullptr);
+    if (!x509_name) return {};
+
+    EXPECT_EQ(1, X509_NAME_add_entry_by_txt(x509_name.get(),  //
+                                            "CN",             //
+                                            MBSTRING_ASC,
+                                            reinterpret_cast<const uint8_t*>(name.c_str()),
+                                            -1,  // len
+                                            -1,  // loc
+                                            0 /* set */));
+
+    int len = i2d_X509_NAME(x509_name.get(), nullptr /* only return length */);
+    EXPECT_GT(len, 0);
+
+    vector<uint8_t> retval(len);
+    uint8_t* p = retval.data();
+    i2d_X509_NAME(x509_name.get(), &p);
+
+    return retval;
+}
+
+namespace {
+
+void check_cose_key(const vector<uint8_t>& data, bool testMode) {
+    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(data);
+    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+
+    // The following check assumes that canonical CBOR encoding is used for the COSE_Key.
+    if (testMode) {
+        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+                    MatchesRegex("{\n"
+                                 "  1 : 2,\n"   // kty: EC2
+                                 "  3 : -7,\n"  // alg: ES256
+                                 "  -1 : 1,\n"  // EC id: P256
+                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
+                                 // separated by commas. In this case, some Ed25519 public key.
+                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
+                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
+                                 "  -70000 : null,\n"                              // test marker
+                                 "}"));
+    } else {
+        EXPECT_THAT(cppbor::prettyPrint(parsedPayload.get()),
+                    MatchesRegex("{\n"
+                                 "  1 : 2,\n"   // kty: EC2
+                                 "  3 : -7,\n"  // alg: ES256
+                                 "  -1 : 1,\n"  // EC id: P256
+                                 // The regex {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}} matches a
+                                 // sequence of 32 hexadecimal bytes, enclosed in braces and
+                                 // separated by commas. In this case, some Ed25519 public key.
+                                 "  -2 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_x: data
+                                 "  -3 : {(0x[0-9a-f]{2}, ){31}0x[0-9a-f]{2}},\n"  // pub_y: data
+                                 "}"));
+    }
+}
+
+}  // namespace
+
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+                        vector<uint8_t>* payload_value) {
+    auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+    ASSERT_TRUE(coseMac0) << "COSE Mac0 parse failed " << mac0ParseErr;
+
+    ASSERT_NE(coseMac0->asArray(), nullptr);
+    ASSERT_EQ(coseMac0->asArray()->size(), kCoseMac0EntryCount);
+
+    auto protParms = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+    ASSERT_NE(protParms, nullptr);
+
+    // Header label:value of 'alg': HMAC-256
+    ASSERT_EQ(cppbor::prettyPrint(protParms->value()), "{\n  1 : 5,\n}");
+
+    auto unprotParms = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+    ASSERT_NE(unprotParms, nullptr);
+    ASSERT_EQ(unprotParms->size(), 0);
+
+    // The payload is a bstr holding an encoded COSE_Key
+    auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+    ASSERT_NE(payload, nullptr);
+    check_cose_key(payload->value(), testMode);
+
+    auto coseMac0Tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+    ASSERT_TRUE(coseMac0Tag);
+    auto extractedTag = coseMac0Tag->value();
+    EXPECT_EQ(extractedTag.size(), 32U);
+
+    // Compare with tag generated with kTestMacKey.  Should only match in test mode
+    auto macFunction = [](const cppcose::bytevec& input) {
+        return cppcose::generateHmacSha256(remote_prov::kTestMacKey, input);
+    };
+    auto testTag =
+            cppcose::generateCoseMac0Mac(macFunction, {} /* external_aad */, payload->value());
+    ASSERT_TRUE(testTag) << "Tag calculation failed: " << testTag.message();
+
+    if (testMode) {
+        EXPECT_THAT(*testTag, ElementsAreArray(extractedTag));
+    } else {
+        EXPECT_THAT(*testTag, Not(ElementsAreArray(extractedTag)));
+    }
+    if (payload_value != nullptr) {
+        *payload_value = payload->value();
+    }
+}
+
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey) {
+    // Extract x and y affine coordinates from the encoded Cose_Key.
+    auto [parsedPayload, __, payloadParseErr] = cppbor::parse(coseKeyData);
+    ASSERT_TRUE(parsedPayload) << "Key parse failed: " << payloadParseErr;
+    auto coseKey = parsedPayload->asMap();
+    const std::unique_ptr<cppbor::Item>& xItem = coseKey->get(cppcose::CoseKey::PUBKEY_X);
+    ASSERT_NE(xItem->asBstr(), nullptr);
+    vector<uint8_t> x = xItem->asBstr()->value();
+    const std::unique_ptr<cppbor::Item>& yItem = coseKey->get(cppcose::CoseKey::PUBKEY_Y);
+    ASSERT_NE(yItem->asBstr(), nullptr);
+    vector<uint8_t> y = yItem->asBstr()->value();
+
+    // Concatenate: 0x04 (uncompressed form marker) | x | y
+    vector<uint8_t> pubKeyData{0x04};
+    pubKeyData.insert(pubKeyData.end(), x.begin(), x.end());
+    pubKeyData.insert(pubKeyData.end(), y.begin(), y.end());
+
+    EC_KEY_Ptr ecKey = EC_KEY_Ptr(EC_KEY_new());
+    ASSERT_NE(ecKey, nullptr);
+    EC_GROUP_Ptr group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
+    ASSERT_NE(group, nullptr);
+    ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+    EC_POINT_Ptr point = EC_POINT_Ptr(EC_POINT_new(group.get()));
+    ASSERT_NE(point, nullptr);
+    ASSERT_EQ(EC_POINT_oct2point(group.get(), point.get(), pubKeyData.data(), pubKeyData.size(),
+                                 nullptr),
+              1);
+    ASSERT_EQ(EC_KEY_set_public_key(ecKey.get(), point.get()), 1);
+
+    EVP_PKEY_Ptr pubKey = EVP_PKEY_Ptr(EVP_PKEY_new());
+    ASSERT_NE(pubKey, nullptr);
+    EVP_PKEY_assign_EC_KEY(pubKey.get(), ecKey.release());
+    *signingKey = std::move(pubKey);
+}
+
+}  // namespace test
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
new file mode 100644
index 0000000..ec3fcf6
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintAidlTestBase.h
@@ -0,0 +1,363 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <functional>
+#include <string_view>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android-base/properties.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <openssl/x509.h>
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
+
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+::std::ostream& operator<<(::std::ostream& os, const AuthorizationSet& set);
+
+inline bool operator==(const keymint::AuthorizationSet& a, const keymint::AuthorizationSet& b) {
+    return a.size() == b.size() && std::equal(a.begin(), a.end(), b.begin());
+}
+
+namespace test {
+
+using ::android::sp;
+using Status = ::ndk::ScopedAStatus;
+using ::std::optional;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+constexpr uint64_t kOpHandleSentinel = 0xFFFFFFFFFFFFFFFF;
+
+class KeyMintAidlTestBase : public ::testing::TestWithParam<string> {
+  public:
+    struct KeyData {
+        vector<uint8_t> blob;
+        vector<KeyCharacteristics> characteristics;
+    };
+
+    static bool arm_deleteAllKeys;
+    static bool dump_Attestations;
+
+    void SetUp() override;
+    void TearDown() override {
+        if (key_blob_.size()) {
+            CheckedDeleteKey();
+        }
+        AbortIfNeeded();
+    }
+
+    void InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint);
+    IKeyMintDevice& keyMint() { return *keymint_; }
+    uint32_t os_version() { return os_version_; }
+    uint32_t os_patch_level() { return os_patch_level_; }
+    uint32_t vendor_patch_level() { return vendor_patch_level_; }
+    uint32_t boot_patch_level(const vector<KeyCharacteristics>& key_characteristics);
+    uint32_t boot_patch_level();
+
+    ErrorCode GetReturnErrorCode(const Status& result);
+
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc, vector<uint8_t>* key_blob,
+                          vector<KeyCharacteristics>* key_characteristics) {
+        return GenerateKey(key_desc, std::nullopt /* attest_key */, key_blob, key_characteristics,
+                           &cert_chain_);
+    }
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+                          const optional<AttestationKey>& attest_key, vector<uint8_t>* key_blob,
+                          vector<KeyCharacteristics>* key_characteristics,
+                          vector<Certificate>* cert_chain);
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+                          const optional<AttestationKey>& attest_key = std::nullopt);
+
+    ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                        const string& key_material, vector<uint8_t>* key_blob,
+                        vector<KeyCharacteristics>* key_characteristics);
+    ErrorCode ImportKey(const AuthorizationSet& key_desc, KeyFormat format,
+                        const string& key_material);
+
+    ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
+                               const AuthorizationSet& wrapping_key_desc, string masking_key,
+                               const AuthorizationSet& unwrapping_params, int64_t password_sid,
+                               int64_t biometric_sid);
+    ErrorCode ImportWrappedKey(string wrapped_key, string wrapping_key,
+                               const AuthorizationSet& wrapping_key_desc, string masking_key,
+                               const AuthorizationSet& unwrapping_params) {
+        return ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, masking_key,
+                                unwrapping_params, 0 /* password_sid */, 0 /* biometric_sid */);
+    }
+
+    ErrorCode GetCharacteristics(const vector<uint8_t>& key_blob, const vector<uint8_t>& app_id,
+                                 const vector<uint8_t>& app_data,
+                                 vector<KeyCharacteristics>* key_characteristics);
+    ErrorCode GetCharacteristics(const vector<uint8_t>& key_blob,
+                                 vector<KeyCharacteristics>* key_characteristics);
+
+    void CheckCharacteristics(const vector<uint8_t>& key_blob,
+                              const vector<KeyCharacteristics>& generate_characteristics);
+    void CheckAppIdCharacteristics(const vector<uint8_t>& key_blob, std::string_view app_id_string,
+                                   std::string_view app_data_string,
+                                   const vector<KeyCharacteristics>& generate_characteristics);
+
+    ErrorCode DeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
+    ErrorCode DeleteKey(bool keep_key_blob = false);
+
+    ErrorCode DeleteAllKeys();
+
+    ErrorCode DestroyAttestationIds();
+
+    void CheckedDeleteKey(vector<uint8_t>* key_blob, bool keep_key_blob = false);
+    void CheckedDeleteKey();
+
+    ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                    const AuthorizationSet& in_params, AuthorizationSet* out_params,
+                    std::shared_ptr<IKeyMintOperation>& op);
+    ErrorCode Begin(KeyPurpose purpose, const vector<uint8_t>& key_blob,
+                    const AuthorizationSet& in_params, AuthorizationSet* out_params);
+    ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+                    AuthorizationSet* out_params);
+    ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params);
+
+    ErrorCode UpdateAad(const string& input);
+    ErrorCode Update(const string& input, string* output);
+
+    ErrorCode Finish(const string& message, const string& signature, string* output);
+    ErrorCode Finish(const string& message, string* output) {
+        return Finish(message, {} /* signature */, output);
+    }
+    ErrorCode Finish(string* output) { return Finish({} /* message */, output); }
+
+    ErrorCode Abort();
+    ErrorCode Abort(const shared_ptr<IKeyMintOperation>& op);
+    void AbortIfNeeded();
+
+    string ProcessMessage(const vector<uint8_t>& key_blob, KeyPurpose operation,
+                          const string& message, const AuthorizationSet& in_params,
+                          AuthorizationSet* out_params);
+    std::tuple<ErrorCode, std::string /* processedMessage */> ProcessMessage(
+            const vector<uint8_t>& key_blob, KeyPurpose operation, const std::string& message,
+            const AuthorizationSet& in_params);
+    string SignMessage(const vector<uint8_t>& key_blob, const string& message,
+                       const AuthorizationSet& params);
+    string SignMessage(const string& message, const AuthorizationSet& params);
+
+    string MacMessage(const string& message, Digest digest, size_t mac_length);
+
+    void CheckHmacTestVector(const string& key, const string& message, Digest digest,
+                             const string& expected_mac);
+
+    void CheckAesCtrTestVector(const string& key, const string& nonce, const string& message,
+                               const string& expected_ciphertext);
+
+    void CheckTripleDesTestVector(KeyPurpose purpose, BlockMode block_mode,
+                                  PaddingMode padding_mode, const string& key, const string& iv,
+                                  const string& input, const string& expected_output);
+
+    void VerifyMessage(const vector<uint8_t>& key_blob, const string& message,
+                       const string& signature, const AuthorizationSet& params);
+    void VerifyMessage(const string& message, const string& signature,
+                       const AuthorizationSet& params);
+    void LocalVerifyMessage(const string& message, const string& signature,
+                            const AuthorizationSet& params);
+
+    string LocalRsaEncryptMessage(const string& message, const AuthorizationSet& params);
+    string EncryptMessage(const vector<uint8_t>& key_blob, const string& message,
+                          const AuthorizationSet& in_params, AuthorizationSet* out_params);
+    string EncryptMessage(const string& message, const AuthorizationSet& params,
+                          AuthorizationSet* out_params);
+    string EncryptMessage(const string& message, const AuthorizationSet& params);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          vector<uint8_t>* iv_out);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          const vector<uint8_t>& iv_in);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          uint8_t mac_length_bits, const vector<uint8_t>& iv_in);
+    string EncryptMessage(const string& message, BlockMode block_mode, PaddingMode padding,
+                          uint8_t mac_length_bits);
+
+    string DecryptMessage(const vector<uint8_t>& key_blob, const string& ciphertext,
+                          const AuthorizationSet& params);
+    string DecryptMessage(const string& ciphertext, const AuthorizationSet& params);
+    string DecryptMessage(const string& ciphertext, BlockMode block_mode, PaddingMode padding_mode,
+                          const vector<uint8_t>& iv);
+
+    std::pair<ErrorCode, vector<uint8_t>> UpgradeKey(const vector<uint8_t>& key_blob);
+
+    template <typename TagType>
+    std::tuple<KeyData /* aesKey */, KeyData /* hmacKey */, KeyData /* rsaKey */,
+               KeyData /* ecdsaKey */>
+    CreateTestKeys(
+            TagType tagToTest, ErrorCode expectedReturn,
+            std::function<void(AuthorizationSetBuilder*)> tagModifier =
+                    [](AuthorizationSetBuilder*) {}) {
+        /* AES */
+        KeyData aesKeyData;
+        AuthorizationSetBuilder aesBuilder = AuthorizationSetBuilder()
+                                                     .AesEncryptionKey(128)
+                                                     .Authorization(tagToTest)
+                                                     .BlockMode(BlockMode::ECB)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED);
+        tagModifier(&aesBuilder);
+        ErrorCode errorCode =
+                GenerateKey(aesBuilder, &aesKeyData.blob, &aesKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* HMAC */
+        KeyData hmacKeyData;
+        AuthorizationSetBuilder hmacBuilder = AuthorizationSetBuilder()
+                                                      .HmacKey(128)
+                                                      .Authorization(tagToTest)
+                                                      .Digest(Digest::SHA_2_256)
+                                                      .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                      .Authorization(TAG_NO_AUTH_REQUIRED);
+        tagModifier(&hmacBuilder);
+        errorCode = GenerateKey(hmacBuilder, &hmacKeyData.blob, &hmacKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* RSA */
+        KeyData rsaKeyData;
+        AuthorizationSetBuilder rsaBuilder = AuthorizationSetBuilder()
+                                                     .RsaSigningKey(2048, 65537)
+                                                     .Authorization(tagToTest)
+                                                     .Digest(Digest::NONE)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .SetDefaultValidity();
+        tagModifier(&rsaBuilder);
+        errorCode = GenerateKey(rsaBuilder, &rsaKeyData.blob, &rsaKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+
+        /* ECDSA */
+        KeyData ecdsaKeyData;
+        AuthorizationSetBuilder ecdsaBuilder = AuthorizationSetBuilder()
+                                                       .EcdsaSigningKey(EcCurve::P_256)
+                                                       .Authorization(tagToTest)
+                                                       .Digest(Digest::SHA_2_256)
+                                                       .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                       .SetDefaultValidity();
+        tagModifier(&ecdsaBuilder);
+        errorCode = GenerateKey(ecdsaBuilder, &ecdsaKeyData.blob, &ecdsaKeyData.characteristics);
+        EXPECT_EQ(expectedReturn, errorCode);
+        return {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData};
+    }
+    bool IsSecure() const { return securityLevel_ != SecurityLevel::SOFTWARE; }
+    SecurityLevel SecLevel() const { return securityLevel_; }
+
+    vector<uint32_t> ValidKeySizes(Algorithm algorithm);
+    vector<uint32_t> InvalidKeySizes(Algorithm algorithm);
+
+    vector<BlockMode> ValidBlockModes(Algorithm algorithm);
+    vector<PaddingMode> ValidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+    vector<PaddingMode> InvalidPaddingModes(Algorithm algorithm, BlockMode blockMode);
+
+    vector<EcCurve> ValidCurves();
+    vector<EcCurve> InvalidCurves();
+
+    vector<Digest> ValidDigests(bool withNone, bool withMD5);
+
+    static vector<string> build_params() {
+        auto params = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+        return params;
+    }
+
+    std::shared_ptr<IKeyMintOperation> op_;
+    vector<Certificate> cert_chain_;
+    vector<uint8_t> key_blob_;
+    vector<KeyCharacteristics> key_characteristics_;
+
+    const vector<KeyParameter>& SecLevelAuthorizations(
+            const vector<KeyCharacteristics>& key_characteristics);
+    inline const vector<KeyParameter>& SecLevelAuthorizations() {
+        return SecLevelAuthorizations(key_characteristics_);
+    }
+    const vector<KeyParameter>& SecLevelAuthorizations(
+            const vector<KeyCharacteristics>& key_characteristics, SecurityLevel securityLevel);
+
+    ErrorCode UseAesKey(const vector<uint8_t>& aesKeyBlob);
+    ErrorCode UseHmacKey(const vector<uint8_t>& hmacKeyBlob);
+    ErrorCode UseRsaKey(const vector<uint8_t>& rsaKeyBlob);
+    ErrorCode UseEcdsaKey(const vector<uint8_t>& ecdsaKeyBlob);
+
+  protected:
+    std::shared_ptr<IKeyMintDevice> keymint_;
+    uint32_t os_version_;
+    uint32_t os_patch_level_;
+    uint32_t vendor_patch_level_;
+    bool timestamp_token_required_;
+
+    SecurityLevel securityLevel_;
+    string name_;
+    string author_;
+    long challenge_;
+};
+
+// If the given property is available, add it to the tag set under the given tag ID.
+template <Tag tag>
+void add_tag_from_prop(AuthorizationSetBuilder* tags, TypedTag<TagType::BYTES, tag> ttag,
+                       const char* prop) {
+    std::string prop_value = ::android::base::GetProperty(prop, /* default= */ "");
+    if (!prop_value.empty()) {
+        tags->Authorization(ttag, prop_value.data(), prop_value.size());
+    }
+}
+
+vector<uint8_t> build_serial_blob(const uint64_t serial_int);
+void verify_subject(const X509* cert, const string& subject, bool self_signed);
+void verify_serial(X509* cert, const uint64_t expected_serial);
+void verify_subject_and_serial(const Certificate& certificate,  //
+                               const uint64_t expected_serial,  //
+                               const string& subject, bool self_signed);
+
+bool verify_attestation_record(const string& challenge,                //
+                               const string& app_id,                   //
+                               AuthorizationSet expected_sw_enforced,  //
+                               AuthorizationSet expected_hw_enforced,  //
+                               SecurityLevel security_level,
+                               const vector<uint8_t>& attestation_cert);
+
+string bin2hex(const vector<uint8_t>& data);
+X509_Ptr parse_cert_blob(const vector<uint8_t>& blob);
+vector<uint8_t> make_name_from_str(const string& name);
+void check_maced_pubkey(const MacedPublicKey& macedPubKey, bool testMode,
+                        vector<uint8_t>* payload_value);
+void p256_pub_key(const vector<uint8_t>& coseKeyData, EVP_PKEY_Ptr* signingKey);
+
+AuthorizationSet HwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
+AuthorizationSet SwEnforcedAuthorizations(const vector<KeyCharacteristics>& key_characteristics);
+::testing::AssertionResult ChainSignaturesAreValid(const vector<Certificate>& chain,
+                                                   bool strict_issuer_check = true);
+
+#define INSTANTIATE_KEYMINT_AIDL_TEST(name)                                          \
+    INSTANTIATE_TEST_SUITE_P(PerInstance, name,                                      \
+                             testing::ValuesIn(KeyMintAidlTestBase::build_params()), \
+                             ::android::PrintInstanceNameToString);                  \
+    GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(name);
+
+}  // namespace test
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/aidl/vts/functional/KeyMintTest.cpp b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
new file mode 100644
index 0000000..caac346
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/KeyMintTest.cpp
@@ -0,0 +1,6683 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "keymint_1_test"
+#include <cutils/log.h>
+
+#include <signal.h>
+
+#include <algorithm>
+#include <iostream>
+
+#include <openssl/ec.h>
+#include <openssl/evp.h>
+#include <openssl/mem.h>
+#include <openssl/x509v3.h>
+
+#include <cutils/properties.h>
+
+#include <android/binder_manager.h>
+
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/KeyFormat.h>
+
+#include <keymint_support/key_param_output.h>
+#include <keymint_support/openssl_utils.h>
+
+#include "KeyMintAidlTestBase.h"
+
+using aidl::android::hardware::security::keymint::AuthorizationSet;
+using aidl::android::hardware::security::keymint::KeyCharacteristics;
+using aidl::android::hardware::security::keymint::KeyFormat;
+
+namespace std {
+
+using namespace aidl::android::hardware::security::keymint;
+
+template <>
+struct std::equal_to<KeyCharacteristics> {
+    bool operator()(const KeyCharacteristics& a, const KeyCharacteristics& b) const {
+        if (a.securityLevel != b.securityLevel) return false;
+
+        // this isn't very efficient. Oh, well.
+        AuthorizationSet a_auths(a.authorizations);
+        AuthorizationSet b_auths(b.authorizations);
+
+        a_auths.Sort();
+        b_auths.Sort();
+
+        return a_auths == b_auths;
+    }
+};
+
+}  // namespace std
+
+namespace aidl::android::hardware::security::keymint::test {
+
+namespace {
+
+bool check_patchLevels = false;
+
+// The maximum number of times we'll attempt to verify that corruption
+// of an ecrypted blob results in an error. Retries are necessary as there
+// is a small (roughly 1/256) chance that corrupting ciphertext still results
+// in valid PKCS7 padding.
+constexpr size_t kMaxPaddingCorruptionRetries = 8;
+
+template <TagType tag_type, Tag tag, typename ValueT>
+bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag> ttag,
+              ValueT expected_value) {
+    auto it = std::find_if(set.begin(), set.end(), [&](const KeyParameter& param) {
+        if (auto p = authorizationValue(ttag, param)) {
+            return *p == expected_value;
+        }
+        return false;
+    });
+    return (it != set.end());
+}
+
+template <TagType tag_type, Tag tag>
+bool contains(const vector<KeyParameter>& set, TypedTag<tag_type, tag>) {
+    auto it = std::find_if(set.begin(), set.end(),
+                           [&](const KeyParameter& param) { return param.tag == tag; });
+    return (it != set.end());
+}
+
+constexpr char hex_value[256] = {0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 1,  2,  3,  4,  5,  6,  7, 8, 9, 0, 0, 0, 0, 0, 0,  // '0'..'9'
+                                 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'A'..'F'
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0,  // 'a'..'f'
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0,  //
+                                 0, 0,  0,  0,  0,  0,  0,  0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+string hex2str(string a) {
+    string b;
+    size_t num = a.size() / 2;
+    b.resize(num);
+    for (size_t i = 0; i < num; i++) {
+        b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
+    }
+    return b;
+}
+
+string rsa_key = hex2str(
+        // RFC 5208 s5
+        "30820275"            // SEQUENCE length 0x275 (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300d"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 9 (algorithm)
+        "2a864886f70d010101"  // 1.2.840.113549.1.1.1 (rsaEncryption)
+        "0500"                // NULL (parameters)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0482025f"  // OCTET STRING length 0x25f (privateKey) holding...
+        // RFC 8017 A.1.2
+        "3082025b"  // SEQUENCE length 0x25b (RSAPrivateKey) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "028181"    // INTEGER length 0x81 value (modulus) ...
+        "00c6095409047d8634812d5a218176e4"
+        "5c41d60a75b13901f234226cffe77652"
+        "1c5a77b9e389417b71c0b6a44d13afe4"
+        "e4a2805d46c9da2935adb1ff0c1f24ea"
+        "06e62b20d776430a4d435157233c6f91"
+        "6783c30e310fcbd89b85c2d567711697"
+        "85ac12bca244abda72bfb19fc44d27c8"
+        "1e1d92de284f4061edfd99280745ea6d"
+        "25"
+        "0203010001"  // INTEGER length 3 value 0x10001 (publicExponent)
+        "028180"      // INTEGER length 0x80 (privateExponent) value...
+        "1be0f04d9cae3718691f035338308e91"
+        "564b55899ffb5084d2460e6630257e05"
+        "b3ceab02972dfabcd6ce5f6ee2589eb6"
+        "7911ed0fac16e43a444b8c861e544a05"
+        "93365772f8baf6b22fc9e3c5f1024b06"
+        "3ac080a7b2234cf8aee8f6c47bbf4fd3"
+        "ace7240290bef16c0b3f7f3cdd64ce3a"
+        "b5912cf6e32f39ab188358afcccd8081"
+        "0241"  // INTEGER length 0x41 (prime1)
+        "00e4b49ef50f765d3b24dde01aceaaf1"
+        "30f2c76670a91a61ae08af497b4a82be"
+        "6dee8fcdd5e3f7ba1cfb1f0c926b88f8"
+        "8c92bfab137fba2285227b83c342ff7c"
+        "55"
+        "0241"  // INTEGER length 0x41 (prime2)
+        "00ddabb5839c4c7f6bf3d4183231f005"
+        "b31aa58affdda5c79e4cce217f6bc930"
+        "dbe563d480706c24e9ebfcab28a6cdef"
+        "d324b77e1bf7251b709092c24ff501fd"
+        "91"
+        "0240"  // INTEGER length 0x40 (exponent1)
+        "23d4340eda3445d8cd26c14411da6fdc"
+        "a63c1ccd4b80a98ad52b78cc8ad8beb2"
+        "842c1d280405bc2f6c1bea214a1d742a"
+        "b996b35b63a82a5e470fa88dbf823cdd"
+        "0240"  // INTEGER length 0x40 (exponent2)
+        "1b7b57449ad30d1518249a5f56bb9829"
+        "4d4b6ac12ffc86940497a5a5837a6cf9"
+        "46262b494526d328c11e1126380fde04"
+        "c24f916dec250892db09a6d77cdba351"
+        "0240"  // INTEGER length 0x40 (coefficient)
+        "7762cd8f4d050da56bd591adb515d24d"
+        "7ccd32cca0d05f866d583514bd7324d5"
+        "f33645e8ed8b4a1cb3cc4a1d67987399"
+        "f2a09f5b3fb68c88d5e5d90ac33492d6"
+        // } end SEQUENCE (PrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+/*
+ * DER-encoded PKCS#8 format RSA key. Generated using:
+ *
+ * openssl genrsa 2048 | openssl pkcs8 -topk8 -nocrypt -outform der | hexdump -e '30/1  "%02X" "\n"'
+ */
+string rsa_2048_key = hex2str(
+        // RFC 5208 s5
+        "308204BD"            // SEQUENCE length 0x4bd (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300D"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 9 (algorithm)
+        "2A864886F70D010101"  // 1.2.840.113549.1.1.1 (rsaEncryption)
+        "0500"                // NULL (parameters)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "048204A7"  // OCTET STRING length 0x25f (privateKey) holding...
+        // RFC 8017 A.1.2
+        "308204A3"  // SEQUENCE length 0x4a3 (RSAPrivateKey) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "02820101"  // INTEGER length 0x101 value (modulus) ...
+        "00BEBC342B56D443B1299F9A6A7056E8"
+        "0A897E318476A5A18029E63B2ED739A6"
+        "1791D339F58DC763D9D14911F2EDEC38"
+        "3DEE11F6319B44510E7A3ECD9B79B973"
+        "82E49500ACF8117DC89CAF0E621F7775"
+        "6554A2FD4664BFE7AB8B59AB48340DBF"
+        "A27B93B5A81F6ECDEB02D0759307128D"
+        "F3E3BAD4055C8B840216DFAA5700670E"
+        "6C5126F0962FCB70FF308F25049164CC"
+        "F76CC2DA66A7DD9A81A714C2809D6918"
+        "6133D29D84568E892B6FFBF3199BDB14"
+        "383EE224407F190358F111A949552ABA"
+        "6714227D1BD7F6B20DD0CB88F9467B71"
+        "9339F33BFF35B3870B3F62204E4286B0"
+        "948EA348B524544B5F9838F29EE643B0"
+        "79EEF8A713B220D7806924CDF7295070"
+        "C5"
+        "0203010001"  // INTEGER length 3 value 0x10001 (publicExponent)
+        "02820100"    // INTEGER length 0x100 (privateExponent) value...
+        "69F377F35F2F584EF075353CCD1CA997"
+        "38DB3DBC7C7FF35F9366CE176DFD1B13"
+        "5AB10030344ABF5FBECF1D4659FDEF1C"
+        "0FC430834BE1BE3911951377BB3D563A"
+        "2EA9CA8F4AD9C48A8CE6FD516A735C66"
+        "2686C7B4B3C09A7B8354133E6F93F790"
+        "D59EAEB92E84C9A4339302CCE28FDF04"
+        "CCCAFA7DE3F3A827D4F6F7D38E68B0EC"
+        "6AB706645BF074A4E4090D06FB163124"
+        "365FD5EE7A20D350E9958CC30D91326E"
+        "1B292E9EF5DB408EC42DAF737D201497"
+        "04D0A678A0FB5B5446863B099228A352"
+        "D604BA8091A164D01D5AB05397C71EAD"
+        "20BE2A08FC528FE442817809C787FEE4"
+        "AB97F97B9130D022153EDC6EB6CBE7B0"
+        "F8E3473F2E901209B5DB10F93604DB01"
+        "028181"  // INTEGER length 0x81 (prime1)
+        "00E83C0998214941EA4F9293F1B77E2E"
+        "99E6CF305FAF358238E126124FEAF2EB"
+        "9724B2EA7B78E6032343821A80E55D1D"
+        "88FB12D220C3F41A56142FEC85796D19"
+        "17F1E8C774F142B67D3D6E7B7E6B4383"
+        "E94DB5929089DBB346D5BDAB40CC2D96"
+        "EE0409475E175C63BF78CFD744136740"
+        "838127EA723FF3FE7FA368C1311B4A4E"
+        "05"
+        "028181"  // INTEGER length 0x81 (prime2)
+        "00D240FCC0F5D7715CDE21CB2DC86EA1"
+        "46132EA3B06F61FF2AF54BF38473F59D"
+        "ADCCE32B5F4CC32DD0BA6F509347B4B5"
+        "B1B58C39F95E4798CCBB43E83D0119AC"
+        "F532F359CA743C85199F0286610E2009"
+        "97D7312917179AC9B67558773212EC96"
+        "1E8BCE7A3CC809BC5486A96E4B0E6AF3"
+        "94D94E066A0900B7B70E82A44FB30053"
+        "C1"
+        "028181"  // INTEGER length 0x81 (exponent1)
+        "00AD15DA1CBD6A492B66851BA8C316D3"
+        "8AB700E2CFDDD926A658003513C54BAA"
+        "152B30021D667D20078F500F8AD3E7F3"
+        "945D74A891ED1A28EAD0FEEAEC8C14A8"
+        "E834CF46A13D1378C99D18940823CFDD"
+        "27EC5810D59339E0C34198AC638E09C8"
+        "7CBB1B634A9864AE9F4D5EB2D53514F6"
+        "7B4CAEC048C8AB849A02E397618F3271"
+        "35"
+        "028180"  // INTEGER length 0x80 (exponent2)
+        "1FA2C1A5331880A92D8F3E281C617108"
+        "BF38244F16E352E69ED417C7153F9EC3"
+        "18F211839C643DCF8B4DD67CE2AC312E"
+        "95178D5D952F06B1BF779F4916924B70"
+        "F582A23F11304E02A5E7565AE22A35E7"
+        "4FECC8B6FDC93F92A1A37703E4CF0E63"
+        "783BD02EB716A7ECBBFA606B10B74D01"
+        "579522E7EF84D91FC522292108D902C1"
+        "028180"  // INTEGER length 0x80 (coefficient)
+        "796FE3825F9DCC85DF22D58690065D93"
+        "898ACD65C087BEA8DA3A63BF4549B795"
+        "E2CD0E3BE08CDEBD9FCF1720D9CDC507"
+        "0D74F40DED8E1102C52152A31B6165F8"
+        "3A6722AECFCC35A493D7634664B888A0"
+        "8D3EB034F12EA28BFEE346E205D33482"
+        "7F778B16ED40872BD29FCB36536B6E93"
+        "FFB06778696B4A9D81BB0A9423E63DE5"
+        // } end SEQUENCE (PrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+string ec_256_key = hex2str(
+        // RFC 5208 s5
+        "308187"            // SEQUENCE length 0x87 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  //  1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "046d"    // OCTET STRING length 0x6d (privateKey) holding...
+        "306b"    // SEQUENCE length 0x6b (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0420"    // OCTET STRING length 0x20 (privateKey)
+        "737c2ecd7b8d1940bf2930aa9b4ed3ff"
+        "941eed09366bc03299986481f3a4d859"
+        "a144"  // TAG [1] len 0x44 (publicKey) {
+        "03420004bf85d7720d07c25461683bc6"
+        "48b4778a9a14dd8a024e3bdd8c7ddd9a"
+        "b2b528bbc7aa1b51f14ebbbb0bd0ce21"
+        "bcc41c6eb00083cf3376d11fd44949e0"
+        "b2183bfe"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+string ec_521_key = hex2str(
+        // RFC 5208 s5
+        "3081EE"          // SEQUENCE length 0xee (PrivateKeyInfo) {
+        "020100"          // INTEGER length 1 value 0 (version)
+        "3010"            // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+        "0607"            // OBJECT IDENTIFIER length 7 (algorithm)
+        "2A8648CE3D0201"  // 1.2.840.10045.2.1 (ecPublicKey)
+        "0605"            // OBJECT IDENTIFIER length 5 (param)
+        "2B81040023"      //  1.3.132.0.35 (secp521r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0481D6"  // OCTET STRING length 0xd6 (privateKey) holding...
+        "3081D3"  // SEQUENCE length 0xd3 (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0442"    // OCTET STRING length 0x42 (privateKey)
+        "0011458C586DB5DAA92AFAB03F4FE46A"
+        "A9D9C3CE9A9B7A006A8384BEC4C78E8E"
+        "9D18D7D08B5BCFA0E53C75B064AD51C4"
+        "49BAE0258D54B94B1E885DED08ED4FB2"
+        "5CE9"
+        "A18189"  // TAG [1] len 0x89 (publicKey) {
+        "03818600040149EC11C6DF0FA122C6A9"
+        "AFD9754A4FA9513A627CA329E349535A"
+        "5629875A8ADFBE27DCB932C051986377"
+        "108D054C28C6F39B6F2C9AF81802F9F3"
+        "26B842FF2E5F3C00AB7635CFB36157FC"
+        "0882D574A10D839C1A0C049DC5E0D775"
+        "E2EE50671A208431BB45E78E70BEFE93"
+        "0DB34818EE4D5C26259F5C6B8E28A652"
+        "950F9F88D7B4B2C9D9"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+string ec_256_key_rfc5915 = hex2str(
+        // RFC 5208 s5
+        "308193"            // SEQUENCE length 0x93 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  //  1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "0479"  // OCTET STRING length 0x79 (privateKey) holding...
+        // RFC 5915 s3
+        "3077"    // SEQUENCE length 0x77 (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 1 (version)
+        "0420"    // OCTET STRING length 0x42 (privateKey)
+        "782370a8c8ce5537baadd04dcff079c8"
+        "158cfa9c67b818b38e8d21c9fa750c1d"
+        "a00a"              // TAG [0] length 0xa (parameters)
+        "0608"              // OBJECT IDENTIFIER length 8
+        "2a8648ce3d030107"  // 1.2.840.10045.3.1.7 (secp256r1)
+        // } end TAG [0]
+        "a144"  // TAG [1] length 0x44 (publicKey) {
+        "0342"  // BIT STRING length 0x42
+        "00"    // no pad bits
+        "04e2cc561ee701da0ad0ef0d176bb0c9"
+        "19d42e79c393fdc1bd6c4010d85cf2cf"
+        "8e68c905464666f98dad4f01573ba810"
+        "78b3428570a439ba3229fbc026c55068"
+        "2f"
+        // } end SEQUENCE (ECPrivateKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+string ec_256_key_sec1 = hex2str(
+        // RFC 5208 s5
+        "308187"            // SEQUENCE length 0x87 (PrivateKeyInfo) {
+        "020100"            // INTEGER length 1 value 0 (version)
+        "3013"              // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+        "0607"              // OBJECT IDENTIFIER length 7 (algorithm)
+        "2a8648ce3d0201"    // 1.2.840.10045.2.1 (ecPublicKey)
+        "0608"              // OBJECT IDENTIFIER length 8 (param)
+        "2a8648ce3d030107"  // 1.2.840.10045.3.1.7 (secp256r1)
+        // } end SEQUENCE (AlgorithmIdentifier)
+        "046d"  // OCTET STRING length 0x6d (privateKey) holding...
+        // SEC1-v2 C.4
+        "306b"    // SEQUENCE length 0x6b (ECPrivateKey)
+        "020101"  // INTEGER length 1 value 0x01 (version)
+        "0420"    // OCTET STRING length 0x20 (privateKey)
+        "782370a8c8ce5537baadd04dcff079c8"
+        "158cfa9c67b818b38e8d21c9fa750c1d"
+        "a144"  // TAG [1] length 0x44 (publicKey) {
+        "0342"  // BIT STRING length 0x42
+        "00"    // no pad bits
+        "04e2cc561ee701da0ad0ef0d176bb0c9"
+        "19d42e79c393fdc1bd6c4010d85cf2cf"
+        "8e68c905464666f98dad4f01573ba810"
+        "78b3428570a439ba3229fbc026c55068"
+        "2f"
+        // } end TAG [1] (publicKey)
+        // } end SEQUENCE (PrivateKeyInfo)
+);
+
+struct RSA_Delete {
+    void operator()(RSA* p) { RSA_free(p); }
+};
+
+std::string make_string(const uint8_t* data, size_t length) {
+    return std::string(reinterpret_cast<const char*>(data), length);
+}
+
+template <size_t N>
+std::string make_string(const uint8_t (&a)[N]) {
+    return make_string(a, N);
+}
+
+class AidlBuf : public vector<uint8_t> {
+    typedef vector<uint8_t> super;
+
+  public:
+    AidlBuf() {}
+    AidlBuf(const super& other) : super(other) {}
+    AidlBuf(super&& other) : super(std::move(other)) {}
+    explicit AidlBuf(const std::string& other) : AidlBuf() { *this = other; }
+
+    AidlBuf& operator=(const super& other) {
+        super::operator=(other);
+        return *this;
+    }
+
+    AidlBuf& operator=(super&& other) {
+        super::operator=(std::move(other));
+        return *this;
+    }
+
+    AidlBuf& operator=(const string& other) {
+        resize(other.size());
+        for (size_t i = 0; i < other.size(); ++i) {
+            (*this)[i] = static_cast<uint8_t>(other[i]);
+        }
+        return *this;
+    }
+
+    string to_string() const { return string(reinterpret_cast<const char*>(data()), size()); }
+};
+
+string device_suffix(const string& name) {
+    size_t pos = name.find('/');
+    if (pos == string::npos) {
+        return name;
+    }
+    return name.substr(pos + 1);
+}
+
+bool matching_rp_instance(const string& km_name,
+                          std::shared_ptr<IRemotelyProvisionedComponent>* rp) {
+    string km_suffix = device_suffix(km_name);
+
+    vector<string> rp_names =
+            ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
+    for (const string& rp_name : rp_names) {
+        // If the suffix of the RemotelyProvisionedComponent instance equals the suffix of the
+        // KeyMint instance, assume they match.
+        if (device_suffix(rp_name) == km_suffix && AServiceManager_isDeclared(rp_name.c_str())) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(rp_name.c_str()));
+            *rp = IRemotelyProvisionedComponent::fromBinder(binder);
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
+class NewKeyGenerationTest : public KeyMintAidlTestBase {
+  protected:
+    void CheckBaseParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+
+        // Check that some unexpected tags/values are NOT present.
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+    }
+
+    void CheckSymmetricParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+        AuthorizationSet auths = CheckCommonParams(keyCharacteristics);
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::ENCRYPT));
+        EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+
+        EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+    }
+
+    AuthorizationSet CheckCommonParams(const vector<KeyCharacteristics>& keyCharacteristics) {
+        // TODO(swillden): Distinguish which params should be in which auth list.
+        AuthorizationSet auths;
+        for (auto& entry : keyCharacteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+
+        // Verify that App data, ROT and auth timeout are NOT included.
+        EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+        EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+        EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
+
+        // None of the tests specify CREATION_DATETIME so check that the KeyMint implementation
+        // never adds it.
+        EXPECT_FALSE(auths.Contains(TAG_CREATION_DATETIME));
+
+        // Check OS details match the original hardware info.
+        auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
+        EXPECT_TRUE(os_ver);
+        EXPECT_EQ(*os_ver, os_version());
+        auto os_pl = auths.GetTagValue(TAG_OS_PATCHLEVEL);
+        EXPECT_TRUE(os_pl);
+        EXPECT_EQ(*os_pl, os_patch_level());
+
+        if (check_patchLevels) {
+            // Should include vendor and boot patchlevels.
+            auto vendor_pl = auths.GetTagValue(TAG_VENDOR_PATCHLEVEL);
+            EXPECT_TRUE(vendor_pl);
+            EXPECT_EQ(*vendor_pl, vendor_patch_level());
+            auto boot_pl = auths.GetTagValue(TAG_BOOT_PATCHLEVEL);
+            EXPECT_TRUE(boot_pl);
+        }
+
+        return auths;
+    }
+};
+
+/*
+ * NewKeyGenerationTest.Aes
+ *
+ * Verifies that keymint can generate all required AES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Aes) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(builder, &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+                CheckCharacteristics(key_blob, key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::AES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidSize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(builder, &key_blob, &key_characteristics));
+            }
+        }
+    }
+
+    for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            // No key size specified
+            auto builder = AuthorizationSetBuilder()
+                                   .Authorization(TAG_ALGORITHM, Algorithm::AES)
+                                   .BlockMode(block_mode)
+                                   .Padding(padding_mode)
+                                   .SetDefaultValidity();
+            if (block_mode == BlockMode::GCM) {
+                builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+            }
+            EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                      GenerateKey(builder, &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesInvalidPadding
+ *
+ * Verifies that specifying an invalid padding on AES keys gives a failure
+ * somewhere along the way.
+ */
+TEST_P(NewKeyGenerationTest, AesInvalidPadding) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::AES)) {
+            for (auto padding_mode : InvalidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                auto builder = AuthorizationSetBuilder()
+                                       .Authorization(TAG_NO_AUTH_REQUIRED)
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .SetDefaultValidity();
+                if (block_mode == BlockMode::GCM) {
+                    builder.Authorization(TAG_MIN_MAC_LENGTH, 128);
+                }
+
+                auto result = GenerateKey(builder);
+                if (result == ErrorCode::OK) {
+                    // Key creation was OK but has generated a key that cannot be used.
+                    auto params =
+                            AuthorizationSetBuilder().BlockMode(block_mode).Padding(padding_mode);
+                    if (block_mode == BlockMode::GCM) {
+                        params.Authorization(TAG_MAC_LENGTH, 128);
+                    }
+                    auto result = Begin(KeyPurpose::ENCRYPT, params);
+                    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+                                result == ErrorCode::INVALID_KEY_BLOB)
+                            << "unexpected result: " << result;
+                } else {
+                    // The KeyMint implementation detected that the generated key
+                    // is unusable.
+                    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, result);
+                }
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMissingMinMac
+ *
+ * Verifies that specifying an invalid key size for AES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMissingMinMac) {
+    for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+        BlockMode block_mode = BlockMode::GCM;
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            SCOPED_TRACE(testing::Message()
+                         << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            // No MIN_MAC_LENGTH provided.
+            auto builder = AuthorizationSetBuilder()
+                                   .AesEncryptionKey(key_size)
+                                   .BlockMode(block_mode)
+                                   .Padding(padding_mode)
+                                   .SetDefaultValidity();
+            EXPECT_EQ(ErrorCode::MISSING_MIN_MAC_LENGTH,
+                      GenerateKey(builder, &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AesGcmMinMacOutOfRange
+ *
+ * Verifies that specifying an invalid min MAC size for AES key generation returns
+ * UNSUPPORTED_MIN_MAC_LENGTH.
+ */
+TEST_P(NewKeyGenerationTest, AesGcmMinMacOutOfRange) {
+    for (size_t min_mac_len : {88, 136}) {
+        for (auto key_size : ValidKeySizes(Algorithm::AES)) {
+            BlockMode block_mode = BlockMode::GCM;
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "AES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                auto builder = AuthorizationSetBuilder()
+                                       .AesEncryptionKey(key_size)
+                                       .BlockMode(block_mode)
+                                       .Padding(padding_mode)
+                                       .Authorization(TAG_MIN_MAC_LENGTH, min_mac_len)
+                                       .SetDefaultValidity();
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+                          GenerateKey(builder, &key_blob, &key_characteristics));
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDes
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, TripleDes) {
+    for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                             .TripleDesEncryptionKey(key_size)
+                                                             .BlockMode(block_mode)
+                                                             .Padding(padding_mode)
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .SetDefaultValidity(),
+                                                     &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+                CheckCharacteristics(key_blob, key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesWithAttestation
+ *
+ * Verifies that keymint can generate all required 3DES key sizes, and that the resulting keys
+ * have correct characteristics.
+ *
+ * Request attestation, which doesn't help for symmetric keys (as there is no public key to
+ * put in a certificate) but which isn't an error.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesWithAttestation) {
+    for (auto key_size : ValidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+
+                auto challenge = "hello";
+                auto app_id = "foo";
+
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                             .TripleDesEncryptionKey(key_size)
+                                                             .BlockMode(block_mode)
+                                                             .Padding(padding_mode)
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .AttestationChallenge(challenge)
+                                                             .AttestationApplicationId(app_id)
+                                                             .SetDefaultValidity(),
+                                                     &key_blob, &key_characteristics));
+
+                EXPECT_GT(key_blob.size(), 0U);
+                CheckSymmetricParams(key_characteristics);
+                CheckCharacteristics(key_blob, key_characteristics);
+
+                AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+                EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::TRIPLE_DES));
+                EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                        << "Key size " << key_size << "missing";
+
+                CheckedDeleteKey(&key_blob);
+            }
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesInvalidSize
+ *
+ * Verifies that specifying an invalid key size for 3-DES key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesInvalidSize) {
+    for (auto key_size : InvalidKeySizes(Algorithm::TRIPLE_DES)) {
+        for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+            for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+                SCOPED_TRACE(testing::Message()
+                             << "3DES-" << key_size << "-" << block_mode << "-" << padding_mode);
+                vector<uint8_t> key_blob;
+                vector<KeyCharacteristics> key_characteristics;
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .TripleDesEncryptionKey(key_size)
+                                              .BlockMode(block_mode)
+                                              .Padding(padding_mode)
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .SetDefaultValidity(),
+                                      &key_blob, &key_characteristics));
+            }
+        }
+    }
+
+    // Omitting the key size fails.
+    for (auto block_mode : ValidBlockModes(Algorithm::TRIPLE_DES)) {
+        for (auto padding_mode : ValidPaddingModes(Algorithm::AES, block_mode)) {
+            SCOPED_TRACE(testing::Message()
+                         << "3DES-default-" << block_mode << "-" << padding_mode);
+            vector<uint8_t> key_blob;
+            vector<KeyCharacteristics> key_characteristics;
+            ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES)
+                                          .BlockMode(block_mode)
+                                          .Padding(padding_mode)
+                                          .Authorization(TAG_NO_AUTH_REQUIRED)
+                                          .SetDefaultValidity(),
+                                  &key_blob, &key_characteristics));
+        }
+    }
+}
+
+/*
+ * NewKeyGenerationTest.Rsa
+ *
+ * Verifies that keymint can generate all required RSA key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Rsa) {
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(key_size, 65537)
+                                                     .Digest(Digest::NONE)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithAttestation
+ *
+ * Verifies that keymint can generate all required RSA key sizes with attestation, and that the
+ * resulting keys have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 66;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                              sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_[0].encodedCertificate));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithRpkAttestation
+ *
+ * Verifies that keymint can generate all required RSA key sizes, using an attestation key
+ * that has been generated using an associate IRemotelyProvisionedComponent.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithRpkAttestation) {
+    // There should be an IRemotelyProvisionedComponent instance associated with the KeyMint
+    // instance.
+    std::shared_ptr<IRemotelyProvisionedComponent> rp;
+    ASSERT_TRUE(matching_rp_instance(GetParam(), &rp))
+            << "No IRemotelyProvisionedComponent found that matches KeyMint device " << GetParam();
+
+    // Generate a P-256 keypair to use as an attestation key.
+    MacedPublicKey macedPubKey;
+    std::vector<uint8_t> privateKeyBlob;
+    auto status =
+            rp->generateEcdsaP256KeyPair(/* testMode= */ false, &macedPubKey, &privateKeyBlob);
+    ASSERT_TRUE(status.isOk());
+    vector<uint8_t> coseKeyData;
+    check_maced_pubkey(macedPubKey, /* testMode= */ false, &coseKeyData);
+
+    AttestationKey attestation_key;
+    attestation_key.keyBlob = std::move(privateKeyBlob);
+    attestation_key.issuerSubjectName = make_name_from_str("Android Keystore Key");
+
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        auto challenge = "hello";
+        auto app_id = "foo";
+
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .SetDefaultValidity(),
+                              attestation_key, &key_blob, &key_characteristics, &cert_chain_));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        // Attestation by itself is not valid (last entry is not self-signed).
+        EXPECT_FALSE(ChainSignaturesAreValid(cert_chain_));
+
+        // The signature over the attested key should correspond to the P256 public key.
+        X509_Ptr key_cert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+        ASSERT_TRUE(key_cert.get());
+        EVP_PKEY_Ptr signing_pubkey;
+        p256_pub_key(coseKeyData, &signing_pubkey);
+        ASSERT_TRUE(signing_pubkey.get());
+
+        ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
+                << "Verification of attested certificate failed "
+                << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaEncryptionWithAttestation
+ *
+ * Verifies that keymint attestation for RSA encryption keys with challenge and
+ * app id is also successful.
+ */
+TEST_P(NewKeyGenerationTest, RsaEncryptionWithAttestation) {
+    auto key_size = 2048;
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 111166;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaEncryptionKey(key_size, 65537)
+                                  .Padding(PaddingMode::NONE)
+                                  .AttestationChallenge(challenge)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
+
+    ASSERT_GT(key_blob.size(), 0U);
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+
+    EXPECT_TRUE(auths.Contains(TAG_ORIGIN, KeyOrigin::GENERATED));
+    EXPECT_TRUE(auths.Contains(TAG_PURPOSE, KeyPurpose::DECRYPT));
+
+    // Verify that App data and ROT are NOT included.
+    EXPECT_FALSE(auths.Contains(TAG_ROOT_OF_TRUST));
+    EXPECT_FALSE(auths.Contains(TAG_APPLICATION_DATA));
+
+    // Check that some unexpected tags/values are NOT present.
+    EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::SIGN));
+    EXPECT_FALSE(auths.Contains(TAG_PURPOSE, KeyPurpose::VERIFY));
+
+    EXPECT_FALSE(auths.Contains(TAG_AUTH_TIMEOUT, 301U));
+
+    auto os_ver = auths.GetTagValue(TAG_OS_VERSION);
+    ASSERT_TRUE(os_ver);
+    EXPECT_EQ(*os_ver, os_version());
+
+    AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+    EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+    EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+            << "Key size " << key_size << "missing";
+    EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+    EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+    ASSERT_GT(cert_chain_.size(), 0);
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+    EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                          sw_enforced, hw_enforced, SecLevel(),
+                                          cert_chain_[0].encodedCertificate));
+
+    CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithSelfSign
+ *
+ * Verifies that attesting to RSA key generation is successful, and returns
+ * self signed certificate if no challenge is provided.  And signing etc
+ * works as expected.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithSelfSign) {
+    auto subject = "cert subj subj subj subj subj subj 22222222222222222222";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_EQ(cert_chain_.size(), 1);
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithAttestationMissAppId
+ *
+ * Verifies that attesting to RSA checks for missing app ID.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestationMissAppId) {
+    auto challenge = "hello";
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(2048, 65537)
+                                  .Digest(Digest::NONE)
+                                  .Padding(PaddingMode::NONE)
+                                  .AttestationChallenge(challenge)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
+}
+
+/*
+ * NewKeyGenerationTest.RsaWithAttestationAppIdIgnored
+ *
+ * Verifies that attesting to RSA ignores app id if challenge is missing.
+ */
+TEST_P(NewKeyGenerationTest, RsaWithAttestationAppIdIgnored) {
+    auto key_size = 2048;
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 1;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .RsaSigningKey(key_size, 65537)
+                                  .Digest(Digest::NONE)
+                                  .Padding(PaddingMode::NONE)
+                                  .AttestationApplicationId(app_id)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                  .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
+
+    ASSERT_GT(key_blob.size(), 0U);
+    CheckBaseParams(key_characteristics);
+    CheckCharacteristics(key_blob, key_characteristics);
+
+    AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+    EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+    EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+            << "Key size " << key_size << "missing";
+    EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+    EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+    ASSERT_EQ(cert_chain_.size(), 1);
+
+    CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.LimitedUsageRsa
+ *
+ * Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
+ * resulting keys have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, LimitedUsageRsa) {
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .RsaSigningKey(key_size, 65537)
+                                                     .Digest(Digest::NONE)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        // Check the usage count limit tag appears in the authorizations.
+        AuthorizationSet auths;
+        for (auto& entry : key_characteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+                << "key usage count limit " << 1U << " missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.LimitedUsageRsaWithAttestation
+ *
+ * Verifies that KeyMint can generate all required RSA key sizes with limited usage, and that the
+ * resulting keys have correct characteristics and attestation.
+ */
+TEST_P(NewKeyGenerationTest, LimitedUsageRsaWithAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 66;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::RSA));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+        EXPECT_TRUE(crypto_params.Contains(TAG_RSA_PUBLIC_EXPONENT, 65537U));
+
+        // Check the usage count limit tag appears in the authorizations.
+        AuthorizationSet auths;
+        for (auto& entry : key_characteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+                << "key usage count limit " << 1U << " missing";
+
+        // Check the usage count limit tag also appears in the attestation.
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                              sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_[0].encodedCertificate));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.NoInvalidRsaSizes
+ *
+ * Verifies that keymint cannot generate any RSA key sizes that are designated as invalid.
+ */
+TEST_P(NewKeyGenerationTest, NoInvalidRsaSizes) {
+    for (auto key_size : InvalidKeySizes(Algorithm::RSA)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .RsaSigningKey(key_size, 65537)
+                                      .Digest(Digest::NONE)
+                                      .Padding(PaddingMode::NONE)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+    }
+}
+
+/*
+ * NewKeyGenerationTest.RsaNoDefaultSize
+ *
+ * Verifies that failing to specify a key size for RSA key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, RsaNoDefaultSize) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+                                  .Authorization(TAG_RSA_PUBLIC_EXPONENT, 3U)
+                                  .SigningKey()
+                                  .SetDefaultValidity()));
+}
+
+/*
+ * NewKeyGenerationTest.RsaMissingParams
+ *
+ * Verifies that omitting optional tags works.
+ */
+TEST_P(NewKeyGenerationTest, RsaMissingParams) {
+    for (auto key_size : ValidKeySizes(Algorithm::RSA)) {
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(
+                          AuthorizationSetBuilder().RsaKey(key_size, 65537).SetDefaultValidity()));
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * NewKeyGenerationTest.Ecdsa
+ *
+ * Verifies that keymint can generate all required EC key sizes, and that the resulting keys
+ * have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Ecdsa) {
+    for (auto curve : ValidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .Digest(Digest::NONE)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAttestation
+ *
+ * Verifies that for all Ecdsa key sizes, if challenge and app id is provided,
+ * an attestation will be generated.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0xFFFFFFFFFFFFFFFF;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    for (auto curve : ValidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(curve)
+                                      .Digest(Digest::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(app_id)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                              sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_[0].encodedCertificate));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAttestationTags
+ *
+ * Verifies that creation of an attested ECDSA key includes various tags in the
+ * attestation extension.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationTags) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+    uint64_t serial_int = 0x1010;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+    const AuthorizationSetBuilder base_builder =
+            AuthorizationSetBuilder()
+                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                    .EcdsaSigningKey(EcCurve::P_256)
+                    .Digest(Digest::NONE)
+                    .AttestationChallenge(challenge)
+                    .AttestationApplicationId(app_id)
+                    .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                    .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                    .SetDefaultValidity();
+
+    // Various tags that map to fields in the attestation extension ASN.1 schema.
+    auto extra_tags = AuthorizationSetBuilder()
+                              .Authorization(TAG_ROLLBACK_RESISTANCE)
+                              .Authorization(TAG_EARLY_BOOT_ONLY)
+                              .Authorization(TAG_ACTIVE_DATETIME, 1619621648000)
+                              .Authorization(TAG_ORIGINATION_EXPIRE_DATETIME, 1619621648000)
+                              .Authorization(TAG_USAGE_EXPIRE_DATETIME, 1619621999000)
+                              .Authorization(TAG_USAGE_COUNT_LIMIT, 42)
+                              .Authorization(TAG_AUTH_TIMEOUT, 100000)
+                              .Authorization(TAG_ALLOW_WHILE_ON_BODY)
+                              .Authorization(TAG_TRUSTED_USER_PRESENCE_REQUIRED)
+                              .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)
+                              .Authorization(TAG_UNLOCKED_DEVICE_REQUIRED)
+                              .Authorization(TAG_CREATION_DATETIME, 1619621648000);
+    for (const KeyParameter& tag : extra_tags) {
+        SCOPED_TRACE(testing::Message() << "tag-" << tag);
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        AuthorizationSetBuilder builder = base_builder;
+        builder.push_back(tag);
+        auto result = GenerateKey(builder, &key_blob, &key_characteristics);
+        if (result == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE &&
+            tag.tag == TAG_ROLLBACK_RESISTANCE) {
+            continue;
+        }
+        if (result == ErrorCode::UNSUPPORTED_TAG &&
+            (tag.tag == TAG_ALLOW_WHILE_ON_BODY || tag.tag == TAG_TRUSTED_USER_PRESENCE_REQUIRED)) {
+            // Optional tag not supported by this KeyMint implementation.
+            continue;
+        }
+        ASSERT_EQ(result, ErrorCode::OK);
+        ASSERT_GT(key_blob.size(), 0U);
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        if (tag.tag != TAG_ATTESTATION_APPLICATION_ID) {
+            // Expect to find most of the extra tags in the key characteristics
+            // of the generated key (but not for ATTESTATION_APPLICATION_ID).
+            EXPECT_TRUE(hw_enforced.Contains(tag.tag) || sw_enforced.Contains(tag.tag))
+                    << tag << " not in hw:" << hw_enforced << " nor sw:" << sw_enforced;
+        }
+
+        // Verifying the attestation record will check for the specific tag because
+        // it's included in the authorizations.
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id, sw_enforced, hw_enforced,
+                                              SecLevel(), cert_chain_[0].encodedCertificate));
+
+        CheckedDeleteKey(&key_blob);
+    }
+
+    // Device attestation IDs should be rejected for normal attestation requests; these fields
+    // are only used for device unique attestation.
+    auto invalid_tags = AuthorizationSetBuilder()
+                                .Authorization(TAG_ATTESTATION_ID_BRAND, "brand")
+                                .Authorization(TAG_ATTESTATION_ID_DEVICE, "device")
+                                .Authorization(TAG_ATTESTATION_ID_PRODUCT, "product")
+                                .Authorization(TAG_ATTESTATION_ID_SERIAL, "serial")
+                                .Authorization(TAG_ATTESTATION_ID_IMEI, "imei")
+                                .Authorization(TAG_ATTESTATION_ID_MEID, "meid")
+                                .Authorization(TAG_ATTESTATION_ID_MANUFACTURER, "manufacturer")
+                                .Authorization(TAG_ATTESTATION_ID_MODEL, "model");
+    for (const KeyParameter& tag : invalid_tags) {
+        SCOPED_TRACE(testing::Message() << "tag-" << tag);
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        AuthorizationSetBuilder builder =
+                AuthorizationSetBuilder()
+                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                        .EcdsaSigningKey(EcCurve::P_256)
+                        .Digest(Digest::NONE)
+                        .AttestationChallenge(challenge)
+                        .AttestationApplicationId(app_id)
+                        .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                        .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                        .SetDefaultValidity();
+        builder.push_back(tag);
+        ASSERT_EQ(ErrorCode::CANNOT_ATTEST_IDS,
+                  GenerateKey(builder, &key_blob, &key_characteristics));
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAttestationTagNoApplicationId
+ *
+ * Verifies that creation of an attested ECDSA key does not include APPLICATION_ID.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationTagNoApplicationId) {
+    auto challenge = "hello";
+    auto attest_app_id = "foo";
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+    uint64_t serial_int = 0x1010;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    // Earlier versions of the attestation extension schema included a slot:
+    //     applicationId  [601] EXPLICIT OCTET_STRING OPTIONAL,
+    // This should never have been included, and should never be filled in.
+    // Generate an attested key that include APPLICATION_ID and APPLICATION_DATA,
+    // to confirm that this field never makes it into the attestation extension.
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_NO_AUTH_REQUIRED)
+                                      .EcdsaSigningKey(EcCurve::P_256)
+                                      .Digest(Digest::NONE)
+                                      .AttestationChallenge(challenge)
+                                      .AttestationApplicationId(attest_app_id)
+                                      .Authorization(TAG_APPLICATION_ID, "client_id")
+                                      .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics);
+    ASSERT_EQ(result, ErrorCode::OK);
+    ASSERT_GT(key_blob.size(), 0U);
+
+    EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+    ASSERT_GT(cert_chain_.size(), 0);
+    verify_subject_and_serial(cert_chain_[0], serial_int, subject, /* self_signed = */ false);
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+    EXPECT_TRUE(verify_attestation_record(challenge, attest_app_id, sw_enforced, hw_enforced,
+                                          SecLevel(), cert_chain_[0].encodedCertificate));
+
+    // Check that the app id is not in the cert.
+    string app_id = "clientid";
+    std::vector<uint8_t> needle(reinterpret_cast<const uint8_t*>(app_id.data()),
+                                reinterpret_cast<const uint8_t*>(app_id.data()) + app_id.size());
+    ASSERT_EQ(std::search(cert_chain_[0].encodedCertificate.begin(),
+                          cert_chain_[0].encodedCertificate.end(), needle.begin(), needle.end()),
+              cert_chain_[0].encodedCertificate.end());
+
+    CheckedDeleteKey(&key_blob);
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaSelfSignAttestation
+ *
+ * Verifies that if no challenge is provided to an Ecdsa key generation, then
+ * the key will generate a self signed attestation.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaSelfSignAttestation) {
+    auto subject = "cert subj 2";
+    vector<uint8_t> subject_der(make_name_from_str(subject));
+
+    uint64_t serial_int = 0x123456FFF1234;
+    vector<uint8_t> serial_blob(build_serial_blob(serial_int));
+
+    for (auto curve : ValidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .EcdsaSigningKey(curve)
+                                      .Digest(Digest::NONE)
+                                      .Authorization(TAG_CERTIFICATE_SERIAL, serial_blob)
+                                      .Authorization(TAG_CERTIFICATE_SUBJECT, subject_der)
+                                      .SetDefaultValidity(),
+                              &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        verify_subject_and_serial(cert_chain_[0], serial_int, subject, false);
+        ASSERT_EQ(cert_chain_.size(), 1);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAttestationRequireAppId
+ *
+ * Verifies that if attestation challenge is provided to Ecdsa key generation, then
+ * app id must also be provided or else it will fail.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAttestationRequireAppId) {
+    auto challenge = "hello";
+    vector<uint8_t> key_blob;
+    vector<KeyCharacteristics> key_characteristics;
+
+    ASSERT_EQ(ErrorCode::ATTESTATION_APPLICATION_ID_MISSING,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .EcdsaSigningKey(EcCurve::P_256)
+                                  .Digest(Digest::NONE)
+                                  .AttestationChallenge(challenge)
+                                  .SetDefaultValidity(),
+                          &key_blob, &key_characteristics));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaIgnoreAppId
+ *
+ * Verifies that if no challenge is provided to the Ecdsa key generation, then
+ * any appid will be ignored, and keymint will generate a self sign certificate.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaIgnoreAppId) {
+    auto app_id = "foo";
+
+    for (auto curve : ValidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .Digest(Digest::NONE)
+                                                     .AttestationApplicationId(app_id)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_EQ(cert_chain_.size(), 1);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.AttestationApplicationIDLengthProperlyEncoded
+ *
+ * Verifies that the Attestation Application ID software enforced tag has a proper length encoding.
+ * Some implementations break strict encoding rules by encoding a length between 127 and 256 in one
+ * byte. Proper DER encoding specifies that for lengths greater than 127, one byte should be used
+ * to specify how many following bytes will be used to encode the length.
+ */
+TEST_P(NewKeyGenerationTest, AttestationApplicationIDLengthProperlyEncoded) {
+    auto challenge = "hello";
+    std::vector<uint32_t> app_id_lengths{143, 258};
+
+    for (uint32_t length : app_id_lengths) {
+        const string app_id(length, 'a');
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .EcdsaSigningKey(EcCurve::P_256)
+                                                     .Digest(Digest::NONE)
+                                                     .AttestationChallenge(challenge)
+                                                     .AttestationApplicationId(app_id)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, EcCurve::P_256)) << "Curve P256 missing";
+
+        EXPECT_TRUE(ChainSignaturesAreValid(cert_chain_));
+        ASSERT_GT(cert_chain_.size(), 0);
+
+        AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+        AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+        EXPECT_TRUE(verify_attestation_record(challenge, app_id,  //
+                                              sw_enforced, hw_enforced, SecLevel(),
+                                              cert_chain_[0].encodedCertificate));
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.LimitedUsageEcdsa
+ *
+ * Verifies that KeyMint can generate all required EC key sizes with limited usage, and that the
+ * resulting keys have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, LimitedUsageEcdsa) {
+    for (auto curve : ValidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .Digest(Digest::NONE)
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                     .SetDefaultValidity(),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::EC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_EC_CURVE, curve)) << "Curve " << curve << "missing";
+
+        // Check the usage count limit tag appears in the authorizations.
+        AuthorizationSet auths;
+        for (auto& entry : key_characteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+                << "key usage count limit " << 1U << " missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaDefaultSize
+ *
+ * Verifies that failing to specify a curve for EC key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaDefaultSize) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_ALGORITHM, Algorithm::EC)
+                                  .SigningKey()
+                                  .Digest(Digest::NONE)
+                                  .SetDefaultValidity()));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaInvalidSize
+ *
+ * Verifies that specifying an invalid key size for EC key generation returns
+ * UNSUPPORTED_KEY_SIZE.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaInvalidSize) {
+    for (auto curve : InvalidCurves()) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE, GenerateKey(AuthorizationSetBuilder()
+                                                                       .EcdsaSigningKey(curve)
+                                                                       .Digest(Digest::NONE)
+                                                                       .SetDefaultValidity(),
+                                                               &key_blob, &key_characteristics));
+    }
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_ALGORITHM, Algorithm::EC)
+                                  .Authorization(TAG_KEY_SIZE, 190)
+                                  .SigningKey()
+                                  .Digest(Digest::NONE)
+                                  .SetDefaultValidity()));
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaMismatchKeySize
+ *
+ * Verifies that specifying mismatched key size and curve for EC key generation returns
+ * INVALID_ARGUMENT.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaMismatchKeySize) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    auto result = GenerateKey(AuthorizationSetBuilder()
+                                      .Authorization(TAG_KEY_SIZE, 224)
+                                      .Authorization(TAG_EC_CURVE, EcCurve::P_256)
+                                      .Digest(Digest::NONE)
+                                      .SetDefaultValidity());
+    ASSERT_TRUE(result == ErrorCode::INVALID_ARGUMENT ||
+                result == ErrorCode::UNSUPPORTED_ALGORITHM);
+}
+
+/*
+ * NewKeyGenerationTest.EcdsaAllValidCurves
+ *
+ * Verifies that keymint does not support any curve designated as unsupported.
+ */
+TEST_P(NewKeyGenerationTest, EcdsaAllValidCurves) {
+    Digest digest;
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        digest = Digest::SHA_2_256;
+    } else {
+        digest = Digest::SHA_2_512;
+    }
+    for (auto curve : ValidCurves()) {
+        EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .EcdsaSigningKey(curve)
+                                                     .Digest(digest)
+                                                     .SetDefaultValidity()))
+                << "Failed to generate key on curve: " << curve;
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * NewKeyGenerationTest.Hmac
+ *
+ * Verifies that keymint supports all required digests, and that the resulting keys have correct
+ * characteristics.
+ */
+TEST_P(NewKeyGenerationTest, Hmac) {
+    for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        constexpr size_t key_size = 128;
+        ASSERT_EQ(ErrorCode::OK,
+                  GenerateKey(
+                          AuthorizationSetBuilder().HmacKey(key_size).Digest(digest).Authorization(
+                                  TAG_MIN_MAC_LENGTH, 128),
+                          &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacNoAttestation
+ *
+ * Verifies that for Hmac key generation, no attestation will be generated even if challenge
+ * and app id are provided.
+ */
+TEST_P(NewKeyGenerationTest, HmacNoAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        constexpr size_t key_size = 128;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .HmacKey(key_size)
+                                                     .Digest(digest)
+                                                     .AttestationChallenge(challenge)
+                                                     .AttestationApplicationId(app_id)
+                                                     .Authorization(TAG_MIN_MAC_LENGTH, 128),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        ASSERT_EQ(cert_chain_.size(), 0);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.LimitedUsageHmac
+ *
+ * Verifies that KeyMint supports all required digests with limited usage Hmac, and that the
+ * resulting keys have correct characteristics.
+ */
+TEST_P(NewKeyGenerationTest, LimitedUsageHmac) {
+    for (auto digest : ValidDigests(false /* withNone */, true /* withMD5 */)) {
+        vector<uint8_t> key_blob;
+        vector<KeyCharacteristics> key_characteristics;
+        constexpr size_t key_size = 128;
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .HmacKey(key_size)
+                                                     .Digest(digest)
+                                                     .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1),
+                                             &key_blob, &key_characteristics));
+
+        ASSERT_GT(key_blob.size(), 0U);
+        CheckBaseParams(key_characteristics);
+        CheckCharacteristics(key_blob, key_characteristics);
+
+        AuthorizationSet crypto_params = SecLevelAuthorizations(key_characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_ALGORITHM, Algorithm::HMAC));
+        EXPECT_TRUE(crypto_params.Contains(TAG_KEY_SIZE, key_size))
+                << "Key size " << key_size << "missing";
+
+        // Check the usage count limit tag appears in the authorizations.
+        AuthorizationSet auths;
+        for (auto& entry : key_characteristics) {
+            auths.push_back(AuthorizationSet(entry.authorizations));
+        }
+        EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+                << "key usage count limit " << 1U << " missing";
+
+        CheckedDeleteKey(&key_blob);
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckKeySizes
+ *
+ * Verifies that keymint supports all key sizes, and rejects all invalid key sizes.
+ */
+TEST_P(NewKeyGenerationTest, HmacCheckKeySizes) {
+    for (size_t key_size = 0; key_size <= 512; ++key_size) {
+        if (key_size < 64 || key_size % 8 != 0) {
+            // To keep this test from being very slow, we only test a random fraction of
+            // non-byte key sizes.  We test only ~10% of such cases. Since there are 392 of
+            // them, we expect to run ~40 of them in each run.
+            if (key_size % 8 == 0 || random() % 10 == 0) {
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .HmacKey(key_size)
+                                              .Digest(Digest::SHA_2_256)
+                                              .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                        << "HMAC key size " << key_size << " invalid";
+            }
+        } else {
+            EXPECT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                         .HmacKey(key_size)
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                    << "Failed to generate HMAC key of size " << key_size;
+            CheckedDeleteKey();
+        }
+    }
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        // STRONGBOX devices must not support keys larger than 512 bits.
+        size_t key_size = 520;
+        EXPECT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+                  GenerateKey(AuthorizationSetBuilder()
+                                      .HmacKey(key_size)
+                                      .Digest(Digest::SHA_2_256)
+                                      .Authorization(TAG_MIN_MAC_LENGTH, 256)))
+                << "HMAC key size " << key_size << " unexpectedly valid";
+    }
+}
+
+/*
+ * NewKeyGenerationTest.HmacCheckMinMacLengths
+ *
+ * Verifies that keymint supports all required MAC lengths and rejects all invalid lengths. This
+ * test is probabilistic in order to keep the runtime down, but any failure prints out the
+ * specific MAC length that failed, so reproducing a failed run will be easy.
+ */
+TEST_P(NewKeyGenerationTest, HmacCheckMinMacLengths) {
+    for (size_t min_mac_length = 0; min_mac_length <= 256; ++min_mac_length) {
+        if (min_mac_length < 64 || min_mac_length % 8 != 0) {
+            // To keep this test from being very long, we only test a random fraction of
+            // non-byte lengths.  We test only ~10% of such cases. Since there are 172 of them,
+            // we expect to run ~17 of them in each run.
+            if (min_mac_length % 8 == 0 || random() % 10 == 0) {
+                EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+                          GenerateKey(AuthorizationSetBuilder()
+                                              .HmacKey(128)
+                                              .Digest(Digest::SHA_2_256)
+                                              .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+                        << "HMAC min mac length " << min_mac_length << " invalid.";
+            }
+        } else {
+            EXPECT_EQ(ErrorCode::OK,
+                      GenerateKey(AuthorizationSetBuilder()
+                                          .HmacKey(128)
+                                          .Digest(Digest::SHA_2_256)
+                                          .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+                    << "Failed to generate HMAC key with min MAC length " << min_mac_length;
+            CheckedDeleteKey();
+        }
+    }
+
+    // Minimum MAC length must be no more than 512 bits.
+    size_t min_mac_length = 520;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MIN_MAC_LENGTH,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::SHA_2_256)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, min_mac_length)))
+            << "HMAC min mac length " << min_mac_length << " invalid.";
+}
+
+/*
+ * NewKeyGenerationTest.HmacMultipleDigests
+ *
+ * Verifies that keymint rejects HMAC key generation with multiple specified digest algorithms.
+ */
+TEST_P(NewKeyGenerationTest, HmacMultipleDigests) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::SHA1)
+                                  .Digest(Digest::SHA_2_256)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+/*
+ * NewKeyGenerationTest.HmacDigestNone
+ *
+ * Verifies that keymint rejects HMAC key generation with no digest or Digest::NONE
+ */
+TEST_P(NewKeyGenerationTest, HmacDigestNone) {
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder().HmacKey(128).Authorization(TAG_MIN_MAC_LENGTH,
+                                                                               128)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .HmacKey(128)
+                                  .Digest(Digest::NONE)
+                                  .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+}
+
+/*
+ * NewKeyGenerationTest.AesNoAttestation
+ *
+ * Verifies that attestation parameters to AES keys are ignored and generateKey
+ * will succeed.
+ */
+TEST_P(NewKeyGenerationTest, AesNoAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .EcbMode()
+                                                 .Padding(PaddingMode::PKCS7)
+                                                 .AttestationChallenge(challenge)
+                                                 .AttestationApplicationId(app_id)));
+
+    ASSERT_EQ(cert_chain_.size(), 0);
+}
+
+/*
+ * NewKeyGenerationTest.TripleDesNoAttestation
+ *
+ * Verifies that attesting parameters to 3DES keys are ignored and generate key
+ * will be successful.  No attestation should be generated.
+ */
+TEST_P(NewKeyGenerationTest, TripleDesNoAttestation) {
+    auto challenge = "hello";
+    auto app_id = "foo";
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .AttestationChallenge(challenge)
+                                                 .AttestationApplicationId(app_id)));
+    ASSERT_EQ(cert_chain_.size(), 0);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(NewKeyGenerationTest);
+
+typedef KeyMintAidlTestBase SigningOperationsTest;
+
+/*
+ * SigningOperationsTest.RsaSuccess
+ *
+ * Verifies that raw RSA signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SetDefaultValidity()));
+    string message = "12345678901234567890123456789012";
+    string signature = SignMessage(
+            message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+    LocalVerifyMessage(message, signature,
+                       AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaAllPaddingsAndDigests
+ *
+ * Verifies RSA signature/verification for all padding modes and digests.
+ */
+TEST_P(SigningOperationsTest, RsaAllPaddingsAndDigests) {
+    auto authorizations = AuthorizationSetBuilder()
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .RsaSigningKey(2048, 65537)
+                                  .Digest(ValidDigests(true /* withNone */, true /* withMD5 */))
+                                  .Padding(PaddingMode::NONE)
+                                  .Padding(PaddingMode::RSA_PSS)
+                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                  .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(authorizations));
+
+    string message(128, 'a');
+    string corrupt_message(message);
+    ++corrupt_message[corrupt_message.size() / 2];
+
+    for (auto padding :
+         {PaddingMode::NONE, PaddingMode::RSA_PSS, PaddingMode::RSA_PKCS1_1_5_SIGN}) {
+        for (auto digest : ValidDigests(true /* withNone */, true /* withMD5 */)) {
+            if (padding == PaddingMode::NONE && digest != Digest::NONE) {
+                // Digesting only makes sense with padding.
+                continue;
+            }
+
+            if (padding == PaddingMode::RSA_PSS && digest == Digest::NONE) {
+                // PSS requires digesting.
+                continue;
+            }
+
+            string signature =
+                    SignMessage(message, AuthorizationSetBuilder().Digest(digest).Padding(padding));
+            LocalVerifyMessage(message, signature,
+                               AuthorizationSetBuilder().Digest(digest).Padding(padding));
+        }
+    }
+}
+
+/*
+ * SigningOperationsTest.RsaUseRequiresCorrectAppIdAppData
+ *
+ * Verifies that using an RSA key requires the correct app data.
+ */
+TEST_P(SigningOperationsTest, RsaUseRequiresCorrectAppIdAppData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .SetDefaultValidity()));
+
+    CheckAppIdCharacteristics(key_blob_, "clientid", "appdata", key_characteristics_);
+
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha256Success
+ *
+ * Verifies that RSA-PSS signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPssSha256Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SetDefaultValidity()));
+    // Use large message, which won't work without digesting.
+    string message(1024, 'a');
+    string signature = SignMessage(
+            message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS));
+}
+
+/*
+ * SigningOperationsTest.RsaPaddingNoneDoesNotAllowOther
+ *
+ * Verifies that keymint rejects signature operations that specify a padding mode when the key
+ * supports only unpadded operations.
+ */
+TEST_P(SigningOperationsTest, RsaPaddingNoneDoesNotAllowOther) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+    string message = "12345678901234567890123456789012";
+    string signature;
+
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
+ * SigningOperationsTest.NoUserConfirmation
+ *
+ * Verifies that keymint rejects signing operations for keys with
+ * TRUSTED_CONFIRMATION_REQUIRED and no valid confirmation token
+ * presented.
+ */
+TEST_P(SigningOperationsTest, NoUserConfirmation) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_TRUSTED_CONFIRMATION_REQUIRED)
+                                                 .SetDefaultValidity()));
+
+    const string message = "12345678901234567890123456789012";
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    string signature;
+    EXPECT_EQ(ErrorCode::NO_USER_CONFIRMATION, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1Sha256Success
+ *
+ * Verifies that digested RSA-PKCS1 signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1Sha256Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+    string message(1024, 'a');
+    string signature = SignMessage(message, AuthorizationSetBuilder()
+                                                    .Digest(Digest::SHA_2_256)
+                                                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestSuccess
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations succeed.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+    string message(53, 'a');
+    string signature = SignMessage(message, AuthorizationSetBuilder()
+                                                    .Digest(Digest::NONE)
+                                                    .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+}
+
+/*
+ * SigningOperationsTest.RsaPkcs1NoDigestTooLarge
+ *
+ * Verifies that undigested RSA-PKCS1 signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_P(SigningOperationsTest, RsaPkcs1NoDigestTooLong) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+    string message(257, 'a');
+
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string signature;
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.RsaPssSha512TooSmallKey
+ *
+ * Verifies that undigested RSA-PSS signature operations fail with the correct error code when
+ * used with a key that is too small for the message.
+ *
+ * A PSS-padded message is of length salt_size + digest_size + 16 (sizes in bits), and the
+ * keymint specification requires that salt_size == digest_size, so the message will be
+ * digest_size * 2 +
+ * 16. Such a message can only be signed by a given key if the key is at least that size. This
+ * test uses SHA512, which has a digest_size == 512, so the message size is 1040 bits, too large
+ * for a 1024-bit key.
+ */
+TEST_P(SigningOperationsTest, RsaPssSha512TooSmallKey) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .Digest(Digest::SHA_2_512)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .SetDefaultValidity()));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::SHA_2_512)
+                                              .Padding(PaddingMode::RSA_PSS)));
+}
+
+/*
+ * SigningOperationsTest.RsaNoPaddingTooLong
+ *
+ * Verifies that raw RSA signature operations fail with the correct error code when
+ * given a too-long message.
+ */
+TEST_P(SigningOperationsTest, RsaNoPaddingTooLong) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+    // One byte too long
+    string message(2048 / 8 + 1, 'a');
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    string result;
+    ErrorCode finish_error_code = Finish(message, &result);
+    EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
+                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+
+    // Very large message that should exceed the transfer buffer size of any reasonable TEE.
+    message = string(128 * 1024, 'a');
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+    finish_error_code = Finish(message, &result);
+    EXPECT_TRUE(finish_error_code == ErrorCode::INVALID_INPUT_LENGTH ||
+                finish_error_code == ErrorCode::INVALID_ARGUMENT);
+}
+
+/*
+ * SigningOperationsTest.RsaAbort
+ *
+ * Verifies that operations can be aborted correctly.  Uses an RSA signing operation for the
+ * test, but the behavior should be algorithm and purpose-independent.
+ */
+TEST_P(SigningOperationsTest, RsaAbort) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+
+    ASSERT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+    EXPECT_EQ(ErrorCode::OK, Abort());
+
+    // Another abort should fail
+    EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
+
+    // Set to sentinel, so TearDown() doesn't try to abort again.
+    op_.reset();
+}
+
+/*
+ * SigningOperationsTest.RsaNonUniqueParams
+ *
+ * Verifies that an operation with multiple padding modes is rejected.
+ */
+TEST_P(SigningOperationsTest, RsaNonUniqueParams) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Digest(Digest::SHA1)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
+                                                 .SetDefaultValidity()));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Padding(PaddingMode::NONE)
+                                              .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+
+    auto result = Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                                  .Digest(Digest::NONE)
+                                                  .Digest(Digest::SHA1)
+                                                  .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN));
+    ASSERT_TRUE(result == ErrorCode::UNSUPPORTED_DIGEST || result == ErrorCode::INVALID_ARGUMENT);
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)));
+}
+
+/*
+ * SigningOperationsTest.RsaUnsupportedPadding
+ *
+ * Verifies that RSA operations fail with the correct error (but key gen succeeds) when used
+ * with a padding mode inappropriate for RSA.
+ */
+TEST_P(SigningOperationsTest, RsaUnsupportedPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Digest(Digest::SHA_2_256 /* supported digest */)
+                                                 .Padding(PaddingMode::PKCS7)
+                                                 .SetDefaultValidity()));
+    ASSERT_EQ(
+            ErrorCode::UNSUPPORTED_PADDING_MODE,
+            Begin(KeyPurpose::SIGN,
+                  AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::PKCS7)));
+    CheckedDeleteKey();
+
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(
+                      AuthorizationSetBuilder()
+                              .RsaSigningKey(2048, 65537)
+                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                              .Digest(Digest::SHA_2_256 /* supported digest */)
+                              .Padding(PaddingMode::RSA_OAEP) /* padding mode for encryption only */
+                              .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::SHA_2_256)
+                                              .Padding(PaddingMode::RSA_OAEP)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoDigest
+ *
+ * Verifies that RSA PSS operations fail when no digest is used.  PSS requires a digest.
+ */
+TEST_P(SigningOperationsTest, RsaNoDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::RSA_PSS)));
+
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS)));
+}
+
+/*
+ * SigningOperationsTest.RsaPssNoPadding
+ *
+ * Verifies that RSA operations fail when no padding mode is specified.  PaddingMode::NONE is
+ * supported in some cases (as validated in other tests), but a mode must be specified.
+ */
+TEST_P(SigningOperationsTest, RsaNoPadding) {
+    // Padding must be specified
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .RsaKey(2048, 65537)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SigningKey()
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaShortMessage
+ *
+ * Verifies that raw RSA signatures succeed with a message shorter than the key size.
+ */
+TEST_P(SigningOperationsTest, RsaTooShortMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+
+    // Barely shorter
+    string message(2048 / 8 - 1, 'a');
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+
+    // Much shorter
+    message = "a";
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE));
+}
+
+/*
+ * SigningOperationsTest.RsaSignWithEncryptionKey
+ *
+ * Verifies that RSA encryption keys cannot be used to sign.
+ */
+TEST_P(SigningOperationsTest, RsaSignWithEncryptionKey) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              Begin(KeyPurpose::SIGN,
+                    AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * SigningOperationsTest.RsaSignTooLargeMessage
+ *
+ * Verifies that attempting a raw signature of a message which is the same length as the key,
+ * but numerically larger than the public modulus, fails with the correct error.
+ */
+TEST_P(SigningOperationsTest, RsaSignTooLargeMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(2048, 65537)
+                                                 .Digest(Digest::NONE)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+
+    // Largest possible message will always be larger than the public modulus.
+    string message(2048 / 8, static_cast<char>(0xff));
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .Digest(Digest::NONE)
+                                                             .Padding(PaddingMode::NONE)));
+    string signature;
+    ASSERT_EQ(ErrorCode::INVALID_ARGUMENT, Finish(message, &signature));
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllDigestsAndCurves
+ *
+ * Verifies ECDSA signature/verification for all digests and curves.
+ */
+TEST_P(SigningOperationsTest, EcdsaAllDigestsAndCurves) {
+    auto digests = ValidDigests(true /* withNone */, false /* withMD5 */);
+
+    string message = "1234567890";
+    string corrupt_message = "2234567890";
+    for (auto curve : ValidCurves()) {
+        SCOPED_TRACE(testing::Message() << "Curve::" << curve);
+        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .EcdsaSigningKey(curve)
+                                              .Digest(digests)
+                                              .SetDefaultValidity());
+        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate key for EC curve " << curve;
+        if (error != ErrorCode::OK) {
+            continue;
+        }
+
+        for (auto digest : digests) {
+            SCOPED_TRACE(testing::Message() << "Digest::" << digest);
+            string signature = SignMessage(message, AuthorizationSetBuilder().Digest(digest));
+            LocalVerifyMessage(message, signature, AuthorizationSetBuilder().Digest(digest));
+        }
+
+        auto rc = DeleteKey();
+        ASSERT_TRUE(rc == ErrorCode::OK || rc == ErrorCode::UNIMPLEMENTED);
+    }
+}
+
+/*
+ * SigningOperationsTest.EcdsaAllCurves
+ *
+ * Verifies that ECDSA operations succeed with all possible curves.
+ */
+TEST_P(SigningOperationsTest, EcdsaAllCurves) {
+    for (auto curve : ValidCurves()) {
+        ErrorCode error = GenerateKey(AuthorizationSetBuilder()
+                                              .Authorization(TAG_NO_AUTH_REQUIRED)
+                                              .EcdsaSigningKey(curve)
+                                              .Digest(Digest::SHA_2_256)
+                                              .SetDefaultValidity());
+        EXPECT_EQ(ErrorCode::OK, error) << "Failed to generate ECDSA key with curve " << curve;
+        if (error != ErrorCode::OK) continue;
+
+        string message(1024, 'a');
+        SignMessage(message, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * SigningOperationsTest.EcdsaNoDigestHugeData
+ *
+ * Verifies that ECDSA operations support very large messages, even without digesting.  This
+ * should work because ECDSA actually only signs the leftmost L_n bits of the message, however
+ * large it may be.  Not using digesting is a bad idea, but in some cases digesting is done by
+ * the framework.
+ */
+TEST_P(SigningOperationsTest, EcdsaNoDigestHugeData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+    string message(1 * 1024, 'a');
+    SignMessage(message, AuthorizationSetBuilder().Digest(Digest::NONE));
+}
+
+/*
+ * SigningOperationsTest.EcUseRequiresCorrectAppIdAppData
+ *
+ * Verifies that using an EC key requires the correct app ID/data.
+ */
+TEST_P(SigningOperationsTest, EcUseRequiresCorrectAppIdAppData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::NONE)
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .SetDefaultValidity()));
+
+    CheckAppIdCharacteristics(key_blob_, "clientid", "appdata", key_characteristics_);
+
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::NONE)));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")));
+    AbortIfNeeded();
+    EXPECT_EQ(ErrorCode::OK,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder()
+                                              .Digest(Digest::NONE)
+                                              .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                              .Authorization(TAG_APPLICATION_ID, "clientid")));
+    AbortIfNeeded();
+}
+
+/*
+ * SigningOperationsTest.EcdsaIncompatibleDigest
+ *
+ * Verifies that using an EC key requires compatible digest.
+ */
+TEST_P(SigningOperationsTest, EcdsaIncompatibleDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::NONE)
+                                                 .Digest(Digest::SHA1)
+                                                 .SetDefaultValidity()));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              Begin(KeyPurpose::SIGN, AuthorizationSetBuilder().Digest(Digest::SHA_2_256)));
+    AbortIfNeeded();
+}
+
+/*
+ * SigningOperationsTest.AesEcbSign
+ *
+ * Verifies that attempts to use AES keys to sign fail in the correct way.
+ */
+TEST_P(SigningOperationsTest, AesEcbSign) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .SigningKey()
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)));
+
+    AuthorizationSet out_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+              Begin(KeyPurpose::SIGN, AuthorizationSet() /* in_params */, &out_params));
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PURPOSE,
+              Begin(KeyPurpose::VERIFY, AuthorizationSet() /* in_params */, &out_params));
+}
+
+/*
+ * SigningOperationsTest.HmacAllDigests
+ *
+ * Verifies that HMAC works with all digests.
+ */
+TEST_P(SigningOperationsTest, HmacAllDigests) {
+    for (auto digest : ValidDigests(false /* withNone */, false /* withMD5 */)) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .HmacKey(128)
+                                                     .Digest(digest)
+                                                     .Authorization(TAG_MIN_MAC_LENGTH, 160)))
+                << "Failed to create HMAC key with digest " << digest;
+        string message = "12345678901234567890123456789012";
+        string signature = MacMessage(message, digest, 160);
+        EXPECT_EQ(160U / 8U, signature.size())
+                << "Failed to sign with HMAC key with digest " << digest;
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooLargeMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC larger than the
+ * digest size.
+ */
+TEST_P(SigningOperationsTest, HmacSha256TooLargeMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                       AuthorizationSetBuilder()
+                                                               .Digest(Digest::SHA_2_256)
+                                                               .Authorization(TAG_MAC_LENGTH, 264),
+                                                       &output_params));
+}
+
+/*
+ * SigningOperationsTest.HmacSha256InvalidMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC whose length is
+ * not a multiple of 8.
+ */
+TEST_P(SigningOperationsTest, HmacSha256InvalidMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 160)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                       AuthorizationSetBuilder()
+                                                               .Digest(Digest::SHA_2_256)
+                                                               .Authorization(TAG_MAC_LENGTH, 161),
+                                                       &output_params));
+}
+
+/*
+ * SigningOperationsTest.HmacSha256TooSmallMacLength
+ *
+ * Verifies that HMAC fails in the correct way when asked to generate a MAC smaller than the
+ * specified minimum MAC length.
+ */
+TEST_P(SigningOperationsTest, HmacSha256TooSmallMacLength) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::SIGN, key_blob_,
+                                                   AuthorizationSetBuilder()
+                                                           .Digest(Digest::SHA_2_256)
+                                                           .Authorization(TAG_MAC_LENGTH, 120),
+                                                   &output_params));
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase3
+ *
+ * Validates against the test vectors from RFC 4231 test case 3.
+ */
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase3) {
+    string key(20, 0xaa);
+    string message(50, 0xdd);
+    uint8_t sha_224_expected[] = {
+            0x7f, 0xb3, 0xcb, 0x35, 0x88, 0xc6, 0xc1, 0xf6, 0xff, 0xa9, 0x69, 0x4d, 0x7d, 0x6a,
+            0xd2, 0x64, 0x93, 0x65, 0xb0, 0xc1, 0xf6, 0x5d, 0x69, 0xd1, 0xec, 0x83, 0x33, 0xea,
+    };
+    uint8_t sha_256_expected[] = {
+            0x77, 0x3e, 0xa9, 0x1e, 0x36, 0x80, 0x0e, 0x46, 0x85, 0x4d, 0xb8,
+            0xeb, 0xd0, 0x91, 0x81, 0xa7, 0x29, 0x59, 0x09, 0x8b, 0x3e, 0xf8,
+            0xc1, 0x22, 0xd9, 0x63, 0x55, 0x14, 0xce, 0xd5, 0x65, 0xfe,
+    };
+    uint8_t sha_384_expected[] = {
+            0x88, 0x06, 0x26, 0x08, 0xd3, 0xe6, 0xad, 0x8a, 0x0a, 0xa2, 0xac, 0xe0,
+            0x14, 0xc8, 0xa8, 0x6f, 0x0a, 0xa6, 0x35, 0xd9, 0x47, 0xac, 0x9f, 0xeb,
+            0xe8, 0x3e, 0xf4, 0xe5, 0x59, 0x66, 0x14, 0x4b, 0x2a, 0x5a, 0xb3, 0x9d,
+            0xc1, 0x38, 0x14, 0xb9, 0x4e, 0x3a, 0xb6, 0xe1, 0x01, 0xa3, 0x4f, 0x27,
+    };
+    uint8_t sha_512_expected[] = {
+            0xfa, 0x73, 0xb0, 0x08, 0x9d, 0x56, 0xa2, 0x84, 0xef, 0xb0, 0xf0, 0x75, 0x6c,
+            0x89, 0x0b, 0xe9, 0xb1, 0xb5, 0xdb, 0xdd, 0x8e, 0xe8, 0x1a, 0x36, 0x55, 0xf8,
+            0x3e, 0x33, 0xb2, 0x27, 0x9d, 0x39, 0xbf, 0x3e, 0x84, 0x82, 0x79, 0xa7, 0x22,
+            0xc8, 0x06, 0xb4, 0x85, 0xa4, 0x7e, 0x67, 0xc8, 0x07, 0xb9, 0x46, 0xa3, 0x37,
+            0xbe, 0xe8, 0x94, 0x26, 0x74, 0x27, 0x88, 0x59, 0xe1, 0x32, 0x92, 0xfb,
+    };
+
+    CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+    if (SecLevel() != SecurityLevel::STRONGBOX) {
+        CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+    }
+}
+
+/*
+ * SigningOperationsTest.HmacRfc4231TestCase5
+ *
+ * Validates against the test vectors from RFC 4231 test case 5.
+ */
+TEST_P(SigningOperationsTest, HmacRfc4231TestCase5) {
+    string key(20, 0x0c);
+    string message = "Test With Truncation";
+
+    uint8_t sha_224_expected[] = {
+            0x0e, 0x2a, 0xea, 0x68, 0xa9, 0x0c, 0x8d, 0x37,
+            0xc9, 0x88, 0xbc, 0xdb, 0x9f, 0xca, 0x6f, 0xa8,
+    };
+    uint8_t sha_256_expected[] = {
+            0xa3, 0xb6, 0x16, 0x74, 0x73, 0x10, 0x0e, 0xe0,
+            0x6e, 0x0c, 0x79, 0x6c, 0x29, 0x55, 0x55, 0x2b,
+    };
+    uint8_t sha_384_expected[] = {
+            0x3a, 0xbf, 0x34, 0xc3, 0x50, 0x3b, 0x2a, 0x23,
+            0xa4, 0x6e, 0xfc, 0x61, 0x9b, 0xae, 0xf8, 0x97,
+    };
+    uint8_t sha_512_expected[] = {
+            0x41, 0x5f, 0xad, 0x62, 0x71, 0x58, 0x0a, 0x53,
+            0x1d, 0x41, 0x79, 0xbc, 0x89, 0x1d, 0x87, 0xa6,
+    };
+
+    CheckHmacTestVector(key, message, Digest::SHA_2_256, make_string(sha_256_expected));
+    if (SecLevel() != SecurityLevel::STRONGBOX) {
+        CheckHmacTestVector(key, message, Digest::SHA_2_224, make_string(sha_224_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_384, make_string(sha_384_expected));
+        CheckHmacTestVector(key, message, Digest::SHA_2_512, make_string(sha_512_expected));
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(SigningOperationsTest);
+
+typedef KeyMintAidlTestBase VerificationOperationsTest;
+
+/*
+ * VerificationOperationsTest.HmacSigningKeyCannotVerify
+ *
+ * Verifies HMAC signing and verification, but that a signing key cannot be used to verify.
+ */
+TEST_P(VerificationOperationsTest, HmacSigningKeyCannotVerify) {
+    string key_material = "HelloThisIsAKey";
+
+    vector<uint8_t> signing_key, verification_key;
+    vector<KeyCharacteristics> signing_key_chars, verification_key_chars;
+    EXPECT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+                                .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+                                .Digest(Digest::SHA_2_256)
+                                .Authorization(TAG_MIN_MAC_LENGTH, 160),
+                        KeyFormat::RAW, key_material, &signing_key, &signing_key_chars));
+    EXPECT_EQ(ErrorCode::OK,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .Authorization(TAG_ALGORITHM, Algorithm::HMAC)
+                                .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
+                                .Digest(Digest::SHA_2_256)
+                                .Authorization(TAG_MIN_MAC_LENGTH, 160),
+                        KeyFormat::RAW, key_material, &verification_key, &verification_key_chars));
+
+    string message = "This is a message.";
+    string signature = SignMessage(
+            signing_key, message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Authorization(TAG_MAC_LENGTH, 160));
+
+    // Signing key should not work.
+    AuthorizationSet out_params;
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE,
+              Begin(KeyPurpose::VERIFY, signing_key,
+                    AuthorizationSetBuilder().Digest(Digest::SHA_2_256), &out_params));
+
+    // Verification key should work.
+    VerifyMessage(verification_key, message, signature,
+                  AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+
+    CheckedDeleteKey(&signing_key);
+    CheckedDeleteKey(&verification_key);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(VerificationOperationsTest);
+
+typedef KeyMintAidlTestBase ExportKeyTest;
+
+/*
+ * ExportKeyTest.RsaUnsupportedKeyFormat
+ *
+ * Verifies that attempting to export RSA keys in PKCS#8 format fails with the correct error.
+ */
+// TODO(seleneh) add ExportKey to GenerateKey
+// check result
+
+class ImportKeyTest : public KeyMintAidlTestBase {
+  public:
+    template <TagType tag_type, Tag tag, typename ValueT>
+    void CheckCryptoParam(TypedTag<tag_type, tag> ttag, ValueT expected) {
+        SCOPED_TRACE("CheckCryptoParam");
+        for (auto& entry : key_characteristics_) {
+            if (entry.securityLevel == SecLevel()) {
+                EXPECT_TRUE(contains(entry.authorizations, ttag, expected))
+                        << "Tag " << tag << " with value " << expected
+                        << " not found at security level" << entry.securityLevel;
+            } else {
+                EXPECT_FALSE(contains(entry.authorizations, ttag, expected))
+                        << "Tag " << tag << " found at security level " << entry.securityLevel;
+            }
+        }
+    }
+
+    void CheckOrigin() {
+        SCOPED_TRACE("CheckOrigin");
+        // Origin isn't a crypto param, but it always lives with them.
+        return CheckCryptoParam(TAG_ORIGIN, KeyOrigin::IMPORTED);
+    }
+};
+
+/*
+ * ImportKeyTest.RsaSuccess
+ *
+ * Verifies that importing and using an RSA key pair works correctly.
+ */
+TEST_P(ImportKeyTest, RsaSuccess) {
+    uint32_t key_size;
+    string key;
+
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        key_size = 2048;
+        key = rsa_2048_key;
+    } else {
+        key_size = 1024;
+        key = rsa_key;
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .RsaSigningKey(key_size, 65537)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_PSS)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_KEY_SIZE, key_size);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+    CheckOrigin();
+
+    string message(1024 / 8, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.RsaSuccessWithoutParams
+ *
+ * Verifies that importing and using an RSA key pair without specifying parameters
+ * works correctly.
+ */
+TEST_P(ImportKeyTest, RsaSuccessWithoutParams) {
+    uint32_t key_size;
+    string key;
+
+    if (SecLevel() == SecurityLevel::STRONGBOX) {
+        key_size = 2048;
+        key = rsa_2048_key;
+    } else {
+        key_size = 1024;
+        key = rsa_key;
+    }
+
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .SigningKey()
+                                               .Authorization(TAG_ALGORITHM, Algorithm::RSA)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Padding(PaddingMode::RSA_PSS)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, key));
+
+    // Key size and public exponent are determined from the imported key material.
+    CheckCryptoParam(TAG_KEY_SIZE, key_size);
+    CheckCryptoParam(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::RSA);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::RSA_PSS);
+    CheckOrigin();
+
+    string message(1024 / 8, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256).Padding(PaddingMode::RSA_PSS);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.RsaKeySizeMismatch
+ *
+ * Verifies that importing an RSA key pair with a size that doesn't match the key fails in the
+ * correct way.
+ */
+TEST_P(ImportKeyTest, RsaKeySizeMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .RsaSigningKey(2048 /* Doesn't match key */, 65537)
+                                .Digest(Digest::NONE)
+                                .Padding(PaddingMode::NONE)
+                                .SetDefaultValidity(),
+                        KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.RsaPublicExponentMismatch
+ *
+ * Verifies that importing an RSA key pair with a public exponent that doesn't match the key
+ * fails in the correct way.
+ */
+TEST_P(ImportKeyTest, RsaPublicExponentMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .RsaSigningKey(1024, 3 /* Doesn't match key */)
+                                .Digest(Digest::NONE)
+                                .Padding(PaddingMode::NONE)
+                                .SetDefaultValidity(),
+                        KeyFormat::PKCS8, rsa_key));
+}
+
+/*
+ * ImportKeyTest.EcdsaSuccess
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair works correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaSuccess) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::P_256)
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ec_256_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaP256RFC5915Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using RFC5915 works
+ * correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaP256RFC5915Success) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::P_256)
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ec_256_key_rfc5915));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaP256SEC1Success
+ *
+ * Verifies that importing and using an ECDSA P-256 key pair encoded using SEC1 works correctly.
+ */
+TEST_P(ImportKeyTest, EcdsaP256SEC1Success) {
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::P_256)
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ec_256_key_sec1));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_256);
+
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.Ecdsa521Success
+ *
+ * Verifies that importing and using an ECDSA P-521 key pair works correctly.
+ */
+TEST_P(ImportKeyTest, Ecdsa521Success) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .EcdsaSigningKey(EcCurve::P_521)
+                                               .Digest(Digest::SHA_2_256)
+                                               .SetDefaultValidity(),
+                                       KeyFormat::PKCS8, ec_521_key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::EC);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckCryptoParam(TAG_EC_CURVE, EcCurve::P_521);
+    CheckOrigin();
+
+    string message(32, 'a');
+    auto params = AuthorizationSetBuilder().Digest(Digest::SHA_2_256);
+    string signature = SignMessage(message, params);
+    LocalVerifyMessage(message, signature, params);
+}
+
+/*
+ * ImportKeyTest.EcdsaCurveMismatch
+ *
+ * Verifies that importing an ECDSA key pair with a curve that doesn't match the key fails in
+ * the correct way.
+ */
+TEST_P(ImportKeyTest, EcdsaCurveMismatch) {
+    ASSERT_EQ(ErrorCode::IMPORT_PARAMETER_MISMATCH,
+              ImportKey(AuthorizationSetBuilder()
+                                .EcdsaSigningKey(EcCurve::P_224 /* Doesn't match key */)
+                                .Digest(Digest::NONE)
+                                .SetDefaultValidity(),
+                        KeyFormat::PKCS8, ec_256_key));
+}
+
+/*
+ * ImportKeyTest.AesSuccess
+ *
+ * Verifies that importing and using an AES key works.
+ */
+TEST_P(ImportKeyTest, AesSuccess) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .AesEncryptionKey(key.size() * 8)
+                                               .EcbMode()
+                                               .Padding(PaddingMode::PKCS7),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::AES);
+    CheckCryptoParam(TAG_KEY_SIZE, 128U);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+    CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.AesFailure
+ *
+ * Verifies that importing an invalid AES key fails.
+ */
+TEST_P(ImportKeyTest, AesFailure) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    uint32_t bitlen = key.size() * 8;
+    for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        // Explicit key size doesn't match that of the provided key.
+        auto result = ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .AesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                                KeyFormat::RAW, key);
+        ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+                    result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+                << "unexpected result: " << result;
+    }
+
+    // Explicit key size matches that of the provided key, but it's not a valid size.
+    string long_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .AesEncryptionKey(long_key.size() * 8)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, long_key));
+    string short_key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .AesEncryptionKey(short_key.size() * 8)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.TripleDesSuccess
+ *
+ * Verifies that importing and using a 3DES key works.
+ */
+TEST_P(ImportKeyTest, TripleDesSuccess) {
+    string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .TripleDesEncryptionKey(168)
+                                               .EcbMode()
+                                               .Padding(PaddingMode::PKCS7),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+    CheckCryptoParam(TAG_KEY_SIZE, 168U);
+    CheckCryptoParam(TAG_PADDING, PaddingMode::PKCS7);
+    CheckCryptoParam(TAG_BLOCK_MODE, BlockMode::ECB);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportKeyTest.TripleDesFailure
+ *
+ * Verifies that importing an invalid 3DES key fails.
+ */
+TEST_P(ImportKeyTest, TripleDesFailure) {
+    string key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358");
+    uint32_t bitlen = key.size() * 7;
+    for (uint32_t key_size : {bitlen - 1, bitlen + 1, bitlen - 8, bitlen + 8}) {
+        // Explicit key size doesn't match that of the provided key.
+        auto result = ImportKey(AuthorizationSetBuilder()
+                                    .Authorization(TAG_NO_AUTH_REQUIRED)
+                                    .TripleDesEncryptionKey(key_size)
+                                    .EcbMode()
+                                    .Padding(PaddingMode::PKCS7),
+                                KeyFormat::RAW, key);
+        ASSERT_TRUE(result == ErrorCode::IMPORT_PARAMETER_MISMATCH ||
+                    result == ErrorCode::UNSUPPORTED_KEY_SIZE)
+                << "unexpected result: " << result;
+    }
+    // Explicit key size matches that of the provided key, but it's not a valid size.
+    string long_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f735800");
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .TripleDesEncryptionKey(long_key.size() * 7)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, long_key));
+    string short_key = hex2str("a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f73");
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_KEY_SIZE,
+              ImportKey(AuthorizationSetBuilder()
+                                .Authorization(TAG_NO_AUTH_REQUIRED)
+                                .TripleDesEncryptionKey(short_key.size() * 7)
+                                .EcbMode()
+                                .Padding(PaddingMode::PKCS7),
+                        KeyFormat::RAW, short_key));
+}
+
+/*
+ * ImportKeyTest.HmacKeySuccess
+ *
+ * Verifies that importing and using an HMAC key works.
+ */
+TEST_P(ImportKeyTest, HmacKeySuccess) {
+    string key = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+    ASSERT_EQ(ErrorCode::OK, ImportKey(AuthorizationSetBuilder()
+                                               .Authorization(TAG_NO_AUTH_REQUIRED)
+                                               .HmacKey(key.size() * 8)
+                                               .Digest(Digest::SHA_2_256)
+                                               .Authorization(TAG_MIN_MAC_LENGTH, 256),
+                                       KeyFormat::RAW, key));
+
+    CheckCryptoParam(TAG_ALGORITHM, Algorithm::HMAC);
+    CheckCryptoParam(TAG_KEY_SIZE, 128U);
+    CheckCryptoParam(TAG_DIGEST, Digest::SHA_2_256);
+    CheckOrigin();
+
+    string message = "Hello World!";
+    string signature = MacMessage(message, Digest::SHA_2_256, 256);
+    VerifyMessage(message, signature, AuthorizationSetBuilder().Digest(Digest::SHA_2_256));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ImportKeyTest);
+
+auto wrapped_key = hex2str(
+        // IKeyMintDevice.aidl
+        "30820179"  // SEQUENCE length 0x179 (SecureKeyWrapper) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "04820100"  // OCTET STRING length 0x100 (encryptedTransportKey)
+        "934bf94e2aa28a3f83c9f79297250262"
+        "fbe3276b5a1c91159bbfa3ef8957aac8"
+        "4b59b30b455a79c2973480823d8b3863"
+        "c3deef4a8e243590268d80e18751a0e1"
+        "30f67ce6a1ace9f79b95e097474febc9"
+        "81195b1d13a69086c0863f66a7b7fdb4"
+        "8792227b1ac5e2489febdf087ab54864"
+        "83033a6f001ca5d1ec1e27f5c30f4cec"
+        "2642074a39ae68aee552e196627a8e3d"
+        "867e67a8c01b11e75f13cca0a97ab668"
+        "b50cda07a8ecb7cd8e3dd7009c963653"
+        "4f6f239cffe1fc8daa466f78b676c711"
+        "9efb96bce4e69ca2a25d0b34ed9c3ff9"
+        "99b801597d5220e307eaa5bee507fb94"
+        "d1fa69f9e519b2de315bac92c36f2ea1"
+        "fa1df4478c0ddedeae8c70e0233cd098"
+        "040c"  // OCTET STRING length 0x0c (initializationVector)
+        "d796b02c370f1fa4cc0124f1"
+        "302e"    // SEQUENCE length 0x2e (KeyDescription) {
+        "020103"  // INTEGER length 1 value 0x03 (keyFormat = RAW)
+        "3029"    // SEQUENCE length 0x29 (AuthorizationList) {
+        "a108"    // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+        "3106"    // SET length 0x06
+        "020100"  // INTEGER length 1 value 0x00 (Encrypt)
+        "020101"  // INTEGER length 1 value 0x01 (Decrypt)
+        // } end SET
+        // } end [1]
+        "a203"    // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+        "020120"  // INTEGER length 1 value 0x20 (AES)
+        // } end [2]
+        "a304"      // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+        "02020100"  // INTEGER length 2 value 0x100
+        // } end [3]
+        "a405"    // [4] context-specific constructed tag=4 length 0x05 { (blockMode)
+        "3103"    // SET length 0x03 {
+        "020101"  // INTEGER length 1 value 0x01 (ECB)
+        // } end SET
+        // } end [4]
+        "a605"    // [6] context-specific constructed tag=6 length 0x05 { (padding)
+        "3103"    // SET length 0x03 {
+        "020140"  // INTEGER length 1 value 0x40 (PKCS7)
+        // } end SET
+        // } end [5]
+        "bf837702"  // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+                    // (noAuthRequired)
+        "0500"      // NULL
+        // } end [503]
+        // } end SEQUENCE (AuthorizationList)
+        // } end SEQUENCE (KeyDescription)
+        "0420"  // OCTET STRING length 0x20 (encryptedKey)
+        "ccd540855f833a5e1480bfd2d36faf3a"
+        "eee15df5beabe2691bc82dde2a7aa910"
+        "0410"  // OCTET STRING length 0x10 (tag)
+        "64c9f689c60ff6223ab6e6999e0eb6e5"
+        // } SEQUENCE (SecureKeyWrapper)
+);
+
+auto wrapped_key_masked = hex2str(
+        // IKeyMintDevice.aidl
+        "30820179"  // SEQUENCE length 0x179 (SecureKeyWrapper) {
+        "020100"    // INTEGER length 1 value 0x00 (version)
+        "04820100"  // OCTET STRING length 0x100 (encryptedTransportKey)
+        "aad93ed5924f283b4bb5526fbe7a1412"
+        "f9d9749ec30db9062b29e574a8546f33"
+        "c88732452f5b8e6a391ee76c39ed1712"
+        "c61d8df6213dec1cffbc17a8c6d04c7b"
+        "30893d8daa9b2015213e219468215532"
+        "07f8f9931c4caba23ed3bee28b36947e"
+        "47f10e0a5c3dc51c988a628daad3e5e1"
+        "f4005e79c2d5a96c284b4b8d7e4948f3"
+        "31e5b85dd5a236f85579f3ea1d1b8484"
+        "87470bdb0ab4f81a12bee42c99fe0df4"
+        "bee3759453e69ad1d68a809ce06b949f"
+        "7694a990429b2fe81e066ff43e56a216"
+        "02db70757922a4bcc23ab89f1e35da77"
+        "586775f423e519c2ea394caf48a28d0c"
+        "8020f1dcf6b3a68ec246f615ae96dae9"
+        "a079b1f6eb959033c1af5c125fd94168"
+        "040c"  // OCTET STRING length 0x0c (initializationVector)
+        "6d9721d08589581ab49204a3"
+        "302e"    // SEQUENCE length 0x2e (KeyDescription) {
+        "020103"  // INTEGER length 1 value 0x03 (keyFormat = RAW)
+        "3029"    // SEQUENCE length 0x29 (AuthorizationList) {
+        "a108"    // [1] context-specific constructed tag=1 length 0x08 { (purpose)
+        "3106"    // SET length 0x06
+        "020100"  // INTEGER length 1 value 0x00 (Encrypt)
+        "020101"  // INTEGER length 1 value 0x01 (Decrypt)
+        // } end SET
+        // } end [1]
+        "a203"    // [2] context-specific constructed tag=2 length 0x02 { (algorithm)
+        "020120"  // INTEGER length 1 value 0x20 (AES)
+        // } end [2]
+        "a304"      // [3] context-specific constructed tag=3 length 0x04 { (keySize)
+        "02020100"  // INTEGER length 2 value 0x100
+        // } end [3]
+        "a405"    // [4] context-specific constructed tag=4 length 0x05 { (blockMode
+        "3103"    // SET length 0x03 {
+        "020101"  // INTEGER length 1 value 0x01 (ECB)
+        // } end SET
+        // } end [4]
+        "a605"    // [6] context-specific constructed tag=6 length 0x05 { (padding)
+        "3103"    // SET length 0x03 {
+        "020140"  // INTEGER length 1 value 0x40 (PKCS7)
+        // } end SET
+        // } end [5]
+        "bf837702"  // [503] context-specific constructed tag=503=0x1F7 length 0x02 {
+                    // (noAuthRequired)
+        "0500"      // NULL
+        // } end [503]
+        // } end SEQUENCE (AuthorizationList)
+        // } end SEQUENCE (KeyDescription)
+        "0420"  // OCTET STRING length 0x20 (encryptedKey)
+        "a61c6e247e25b3e6e69aa78eb03c2d4a"
+        "c20d1f99a9a024a76f35c8e2cab9b68d"
+        "0410"  // OCTET STRING length 0x10 (tag)
+        "2560c70109ae67c030f00b98b512a670"
+        // } SEQUENCE (SecureKeyWrapper)
+);
+
+auto wrapping_key = hex2str(
+        // RFC 5208 s5
+        "308204be"            // SEQUENCE length 0x4be (PrivateKeyInfo) {
+        "020100"              // INTEGER length 1 value 0x00 (version)
+        "300d"                // SEQUENCE length 0x0d (AlgorithmIdentifier) {
+        "0609"                // OBJECT IDENTIFIER length 0x09 (algorithm)
+        "2a864886f70d010101"  // 1.2.840.113549.1.1.1 (RSAES-PKCS1-v1_5 encryption scheme)
+        "0500"                // NULL (parameters)
+        // } SEQUENCE (AlgorithmIdentifier)
+        "048204a8"  // OCTET STRING len 0x4a8 (privateKey), which contains...
+        // RFC 8017 A.1.2
+        "308204a4"                          // SEQUENCE len 0x4a4 (RSAPrivateKey) {
+        "020100"                            // INTEGER length 1 value 0x00 (version)
+        "02820101"                          // INTEGER length 0x0101 (modulus) value...
+        "00aec367931d8900ce56b0067f7d70e1"  // 0x10
+        "fc653f3f34d194c1fed50018fb43db93"  // 0x20
+        "7b06e673a837313d56b1c725150a3fef"  // 0x30
+        "86acbddc41bb759c2854eae32d35841e"  // 0x40
+        "fb5c18d82bc90a1cb5c1d55adf245b02"  // 0x50
+        "911f0b7cda88c421ff0ebafe7c0d23be"  // 0x60
+        "312d7bd5921ffaea1347c157406fef71"  // 0x70
+        "8f682643e4e5d33c6703d61c0cf7ac0b"  // 0x80
+        "f4645c11f5c1374c3886427411c44979"  // 0x90
+        "6792e0bef75dec858a2123c36753e02a"  // 0xa0
+        "95a96d7c454b504de385a642e0dfc3e6"  // 0xb0
+        "0ac3a7ee4991d0d48b0172a95f9536f0"  // 0xc0
+        "2ba13cecccb92b727db5c27e5b2f5cec"  // 0xd0
+        "09600b286af5cf14c42024c61ddfe71c"  // 0xe0
+        "2a8d7458f185234cb00e01d282f10f8f"  // 0xf0
+        "c6721d2aed3f4833cca2bd8fa62821dd"  // 0x100
+        "55"                                // 0x101
+        "0203010001"                        // INTEGER length 3 value 0x10001 (publicExponent)
+        "02820100"                          // INTEGER length 0x100 (privateExponent) value...
+        "431447b6251908112b1ee76f99f3711a"  // 0x10
+        "52b6630960046c2de70de188d833f8b8"  // 0x20
+        "b91e4d785caeeeaf4f0f74414e2cda40"  // 0x30
+        "641f7fe24f14c67a88959bdb27766df9"  // 0x40
+        "e710b630a03adc683b5d2c43080e52be"  // 0x50
+        "e71e9eaeb6de297a5fea1072070d181c"  // 0x60
+        "822bccff087d63c940ba8a45f670feb2"  // 0x70
+        "9fb4484d1c95e6d2579ba02aae0a0090"  // 0x80
+        "0c3ebf490e3d2cd7ee8d0e20c536e4dc"  // 0x90
+        "5a5097272888cddd7e91f228b1c4d747"  // 0xa0
+        "4c55b8fcd618c4a957bbddd5ad7407cc"  // 0xb0
+        "312d8d98a5caf7e08f4a0d6b45bb41c6"  // 0xc0
+        "52659d5a5ba05b663737a8696281865b"  // 0xd0
+        "a20fbdd7f851e6c56e8cbe0ddbbf24dc"  // 0xe0
+        "03b2d2cb4c3d540fb0af52e034a2d066"  // 0xf0
+        "98b128e5f101e3b51a34f8d8b4f86181"  // 0x100
+        "028181"                            // INTEGER length 0x81 (prime1) value...
+        "00de392e18d682c829266cc3454e1d61"  // 0x10
+        "66242f32d9a1d10577753e904ea7d08b"  // 0x20
+        "ff841be5bac82a164c5970007047b8c5"  // 0x30
+        "17db8f8f84e37bd5988561bdf503d4dc"  // 0x40
+        "2bdb38f885434ae42c355f725c9a60f9"  // 0x50
+        "1f0788e1f1a97223b524b5357fdf72e2"  // 0x60
+        "f696bab7d78e32bf92ba8e1864eab122"  // 0x70
+        "9e91346130748a6e3c124f9149d71c74"  // 0x80
+        "35"
+        "028181"                            // INTEGER length 0x81 (prime2) value...
+        "00c95387c0f9d35f137b57d0d65c397c"  // 0x10
+        "5e21cc251e47008ed62a542409c8b6b6"  // 0x20
+        "ac7f8967b3863ca645fcce49582a9aa1"  // 0x30
+        "7349db6c4a95affdae0dae612e1afac9"  // 0x40
+        "9ed39a2d934c880440aed8832f984316"  // 0x50
+        "3a47f27f392199dc1202f9a0f9bd0830"  // 0x60
+        "8007cb1e4e7f58309366a7de25f7c3c9"  // 0x70
+        "b880677c068e1be936e81288815252a8"  // 0x80
+        "a1"
+        "028180"                            // INTEGER length 0x80 (exponent1) value...
+        "57ff8ca1895080b2cae486ef0adfd791"  // 0x10
+        "fb0235c0b8b36cd6c136e52e4085f4ea"  // 0x20
+        "5a063212a4f105a3764743e53281988a"  // 0x30
+        "ba073f6e0027298e1c4378556e0efca0"  // 0x40
+        "e14ece1af76ad0b030f27af6f0ab35fb"  // 0x50
+        "73a060d8b1a0e142fa2647e93b32e36d"  // 0x60
+        "8282ae0a4de50ab7afe85500a16f43a6"  // 0x70
+        "4719d6e2b9439823719cd08bcd031781"  // 0x80
+        "028181"                            // INTEGER length 0x81 (exponent2) value...
+        "00ba73b0bb28e3f81e9bd1c568713b10"  // 0x10
+        "1241acc607976c4ddccc90e65b6556ca"  // 0x20
+        "31516058f92b6e09f3b160ff0e374ec4"  // 0x30
+        "0d78ae4d4979fde6ac06a1a400c61dd3"  // 0x40
+        "1254186af30b22c10582a8a43e34fe94"  // 0x50
+        "9c5f3b9755bae7baa7b7b7a6bd03b38c"  // 0x60
+        "ef55c86885fc6c1978b9cee7ef33da50"  // 0x70
+        "7c9df6b9277cff1e6aaa5d57aca52846"  // 0x80
+        "61"
+        "028181"                            // INTEGER length 0x81 (coefficient) value...
+        "00c931617c77829dfb1270502be9195c"  // 0x10
+        "8f2830885f57dba869536811e6864236"  // 0x20
+        "d0c4736a0008a145af36b8357a7c3d13"  // 0x30
+        "9966d04c4e00934ea1aede3bb6b8ec84"  // 0x40
+        "1dc95e3f579751e2bfdfe27ae778983f"  // 0x50
+        "959356210723287b0affcc9f727044d4"  // 0x60
+        "8c373f1babde0724fa17a4fd4da0902c"  // 0x70
+        "7c9b9bf27ba61be6ad02dfddda8f4e68"  // 0x80
+        "22"
+        // } SEQUENCE
+        // } SEQUENCE ()
+);
+
+string zero_masking_key =
+        hex2str("0000000000000000000000000000000000000000000000000000000000000000");
+string masking_key = hex2str("D796B02C370F1FA4CC0124F14EC8CBEBE987E825246265050F399A51FD477DFC");
+
+class ImportWrappedKeyTest : public KeyMintAidlTestBase {};
+
+TEST_P(ImportWrappedKeyTest, Success) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * ImportWrappedKeyTest.SuccessSidsIgnored
+ *
+ * Verifies that password_sid and biometric_sid are ignored on import if the authorizations don't
+ * include Tag:USER_SECURE_ID.
+ */
+TEST_P(ImportWrappedKeyTest, SuccessSidsIgnored) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    int64_t password_sid = 42;
+    int64_t biometric_sid = 24;
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP),
+                               password_sid, biometric_sid));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    string ciphertext = EncryptMessage(message, params);
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+TEST_P(ImportWrappedKeyTest, SuccessMasked) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::OK,
+              ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongMask) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(
+            ErrorCode::VERIFICATION_FAILED,
+            ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                             AuthorizationSetBuilder()
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongPurpose) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(
+            ErrorCode::INCOMPATIBLE_PURPOSE,
+            ImportWrappedKey(wrapped_key_masked, wrapping_key, wrapping_key_desc, zero_masking_key,
+                             AuthorizationSetBuilder()
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongPaddingMode) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_256)
+                                     .Padding(PaddingMode::RSA_PSS)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
+TEST_P(ImportWrappedKeyTest, WrongDigest) {
+    auto wrapping_key_desc = AuthorizationSetBuilder()
+                                     .RsaEncryptionKey(2048, 65537)
+                                     .Digest(Digest::SHA_2_512)
+                                     .Padding(PaddingMode::RSA_OAEP)
+                                     .Authorization(TAG_PURPOSE, KeyPurpose::WRAP_KEY)
+                                     .SetDefaultValidity();
+
+    ASSERT_EQ(ErrorCode::INCOMPATIBLE_DIGEST,
+              ImportWrappedKey(wrapped_key, wrapping_key, wrapping_key_desc, zero_masking_key,
+                               AuthorizationSetBuilder()
+                                       .Digest(Digest::SHA_2_256)
+                                       .Padding(PaddingMode::RSA_OAEP)));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ImportWrappedKeyTest);
+
+typedef KeyMintAidlTestBase EncryptionOperationsTest;
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingSuccess
+ *
+ * Verifies that raw RSA decryption works.
+ */
+TEST_P(EncryptionOperationsTest, RsaNoPaddingSuccess) {
+    for (uint64_t exponent : {3, 65537}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .RsaEncryptionKey(2048, exponent)
+                                                     .Padding(PaddingMode::NONE)
+                                                     .SetDefaultValidity()));
+
+        string message = string(2048 / 8, 'a');
+        auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+        string ciphertext1 = LocalRsaEncryptMessage(message, params);
+        EXPECT_EQ(2048U / 8, ciphertext1.size());
+
+        string ciphertext2 = LocalRsaEncryptMessage(message, params);
+        EXPECT_EQ(2048U / 8, ciphertext2.size());
+
+        // Unpadded RSA is deterministic
+        EXPECT_EQ(ciphertext1, ciphertext2);
+
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * EncryptionOperationsTest.RsaNoPaddingShortMessage
+ *
+ * Verifies that raw RSA decryption of short messages works.
+ */
+TEST_P(EncryptionOperationsTest, RsaNoPaddingShortMessage) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+
+    string message = "1";
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+
+    string ciphertext = LocalRsaEncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext.size());
+
+    string expected_plaintext = string(2048U / 8 - 1, 0) + message;
+    string plaintext = DecryptMessage(ciphertext, params);
+
+    EXPECT_EQ(expected_plaintext, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepSuccess
+ *
+ * Verifies that RSA-OAEP decryption operations work, with all digests.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepSuccess) {
+    auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+
+    size_t key_size = 2048;  // Need largish key for SHA-512 test.
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .RsaEncryptionKey(key_size, 65537)
+                                  .Padding(PaddingMode::RSA_OAEP)
+                                  .Digest(digests)
+                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA1)
+                                  .SetDefaultValidity()));
+
+    string message = "Hello";
+
+    for (auto digest : digests) {
+        SCOPED_TRACE(testing::Message() << "digest-" << digest);
+
+        auto params = AuthorizationSetBuilder()
+                              .Digest(digest)
+                              .Padding(PaddingMode::RSA_OAEP)
+                              .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA1);
+        string ciphertext1 = LocalRsaEncryptMessage(message, params);
+        if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+        EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+        string ciphertext2 = LocalRsaEncryptMessage(message, params);
+        EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+        // OAEP randomizes padding so every result should be different (with astronomically high
+        // probability).
+        EXPECT_NE(ciphertext1, ciphertext2);
+
+        string plaintext1 = DecryptMessage(ciphertext1, params);
+        EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+        string plaintext2 = DecryptMessage(ciphertext2, params);
+        EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+        // Decrypting corrupted ciphertext should fail.
+        size_t offset_to_corrupt = random() % ciphertext1.size();
+        char corrupt_byte;
+        do {
+            corrupt_byte = static_cast<char>(random() % 256);
+        } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+        ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+        string result;
+        EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+        EXPECT_EQ(0U, result.size());
+    }
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidDigest
+ *
+ * Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
+ * without a digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidDigest) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_OAEP).Digest(Digest::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_DIGEST, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepInvalidPadding
+ *
+ * Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
+ * with a padding value that is only suitable for signing/verifying.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepInvalidPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_PSS)
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PSS).Digest(Digest::NONE);
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_PADDING_MODE, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepDecryptWithWrongDigest
+ *
+ * Verifies that RSA-OAEP decryption operations fail in the correct way when asked to decrypt
+ * with a different digest than was used to encrypt.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepDecryptWithWrongDigest) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(1024, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_224, Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
+    string message = "Hello World!";
+    string ciphertext = LocalRsaEncryptMessage(
+            message,
+            AuthorizationSetBuilder().Digest(Digest::SHA_2_224).Padding(PaddingMode::RSA_OAEP));
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
+                                                                .Digest(Digest::SHA_2_256)
+                                                                .Padding(PaddingMode::RSA_OAEP)));
+    string result;
+    EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext, &result));
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepWithMGFDigestSuccess
+ *
+ * Verifies that RSA-OAEP decryption operations work, with all SHA 256 digests and all type of MGF1
+ * digests.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFDigestSuccess) {
+    auto digests = ValidDigests(false /* withNone */, true /* withMD5 */);
+
+    size_t key_size = 2048;  // Need largish key for SHA-512 test.
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .OaepMGFDigest(digests)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(key_size, 65537)
+                                                 .Padding(PaddingMode::RSA_OAEP)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .SetDefaultValidity()));
+
+    string message = "Hello";
+
+    for (auto digest : digests) {
+        auto params = AuthorizationSetBuilder()
+                              .Authorization(TAG_RSA_OAEP_MGF_DIGEST, digest)
+                              .Digest(Digest::SHA_2_256)
+                              .Padding(PaddingMode::RSA_OAEP);
+        string ciphertext1 = LocalRsaEncryptMessage(message, params);
+        if (HasNonfatalFailure()) std::cout << "-->" << digest << std::endl;
+        EXPECT_EQ(key_size / 8, ciphertext1.size());
+
+        string ciphertext2 = LocalRsaEncryptMessage(message, params);
+        EXPECT_EQ(key_size / 8, ciphertext2.size());
+
+        // OAEP randomizes padding so every result should be different (with astronomically high
+        // probability).
+        EXPECT_NE(ciphertext1, ciphertext2);
+
+        string plaintext1 = DecryptMessage(ciphertext1, params);
+        EXPECT_EQ(message, plaintext1) << "RSA-OAEP failed with digest " << digest;
+        string plaintext2 = DecryptMessage(ciphertext2, params);
+        EXPECT_EQ(message, plaintext2) << "RSA-OAEP failed with digest " << digest;
+
+        // Decrypting corrupted ciphertext should fail.
+        size_t offset_to_corrupt = random() % ciphertext1.size();
+        char corrupt_byte;
+        do {
+            corrupt_byte = static_cast<char>(random() % 256);
+        } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+        ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+        string result;
+        EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+        EXPECT_EQ(0U, result.size());
+    }
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepWithMGFIncompatibleDigest
+ *
+ * Verifies that RSA-OAEP decryption operations fail in the correct way when asked to operate
+ * with incompatible MGF digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFIncompatibleDigest) {
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .RsaEncryptionKey(2048, 65537)
+                                  .Padding(PaddingMode::RSA_OAEP)
+                                  .Digest(Digest::SHA_2_256)
+                                  .SetDefaultValidity()));
+    string message = "Hello World!";
+
+    auto params = AuthorizationSetBuilder()
+                          .Padding(PaddingMode::RSA_OAEP)
+                          .Digest(Digest::SHA_2_256)
+                          .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_224);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_MGF_DIGEST, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaOaepWithMGFUnsupportedDigest
+ *
+ * Verifies that RSA-OAEP encryption operations fail in the correct way when asked to operate
+ * with unsupported MGF digest.
+ */
+TEST_P(EncryptionOperationsTest, RsaOaepWithMGFUnsupportedDigest) {
+    ASSERT_EQ(ErrorCode::OK,
+              GenerateKey(AuthorizationSetBuilder()
+                                  .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::SHA_2_256)
+                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                  .RsaEncryptionKey(2048, 65537)
+                                  .Padding(PaddingMode::RSA_OAEP)
+                                  .Digest(Digest::SHA_2_256)
+                                  .SetDefaultValidity()));
+    string message = "Hello World!";
+
+    auto params = AuthorizationSetBuilder()
+                          .Padding(PaddingMode::RSA_OAEP)
+                          .Digest(Digest::SHA_2_256)
+                          .Authorization(TAG_RSA_OAEP_MGF_DIGEST, Digest::NONE);
+    EXPECT_EQ(ErrorCode::UNSUPPORTED_MGF_DIGEST, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.RsaPkcs1Success
+ *
+ * Verifies that RSA PKCS encryption/decrypts works.
+ */
+TEST_P(EncryptionOperationsTest, RsaPkcs1Success) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT)
+                                                 .SetDefaultValidity()));
+
+    string message = "Hello World!";
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
+    string ciphertext1 = LocalRsaEncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext1.size());
+
+    string ciphertext2 = LocalRsaEncryptMessage(message, params);
+    EXPECT_EQ(2048U / 8, ciphertext2.size());
+
+    // PKCS1 v1.5 randomizes padding so every result should be different.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Decrypting corrupted ciphertext should fail.
+    size_t offset_to_corrupt = random() % ciphertext1.size();
+    char corrupt_byte;
+    do {
+        corrupt_byte = static_cast<char>(random() % 256);
+    } while (corrupt_byte == ciphertext1[offset_to_corrupt]);
+    ciphertext1[offset_to_corrupt] = corrupt_byte;
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    string result;
+    EXPECT_EQ(ErrorCode::UNKNOWN_ERROR, Finish(ciphertext1, &result));
+    EXPECT_EQ(0U, result.size());
+}
+
+/*
+ * EncryptionOperationsTest.EcdsaEncrypt
+ *
+ * Verifies that attempting to use ECDSA keys to encrypt fails in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, EcdsaEncrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .EcdsaSigningKey(EcCurve::P_256)
+                                                 .Digest(Digest::NONE)
+                                                 .SetDefaultValidity()));
+    auto params = AuthorizationSetBuilder().Digest(Digest::NONE);
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.HmacEncrypt
+ *
+ * Verifies that attempting to use HMAC keys to encrypt fails in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, HmacEncrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    auto params = AuthorizationSetBuilder()
+                          .Digest(Digest::SHA_2_256)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::ENCRYPT, params));
+    ASSERT_EQ(ErrorCode::UNSUPPORTED_PURPOSE, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbRoundTripSuccess
+ *
+ * Verifies that AES ECB mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_GT(key_blob_.size(), 0U);
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+
+    // Two-block message.
+    string message = "12345678901234567890123456789012";
+    string ciphertext1 = EncryptMessage(message, params);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(string(message), params);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // ECB is deterministic.
+    EXPECT_EQ(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbUnknownTag
+ *
+ * Verifies that AES ECB operations ignore unknown tags.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbUnknownTag) {
+    int32_t unknown_tag_value = ((7 << 28) /* TagType:BOOL */ | 150);
+    Tag unknown_tag = static_cast<Tag>(unknown_tag_value);
+    KeyParameter unknown_param;
+    unknown_param.tag = unknown_tag;
+
+    vector<KeyCharacteristics> key_characteristics;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(unknown_param),
+                                         &key_blob_, &key_characteristics));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    // Unknown tags should not be returned in key characteristics.
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(key_characteristics);
+    EXPECT_EQ(hw_enforced.find(unknown_tag), -1);
+    EXPECT_EQ(sw_enforced.find(unknown_tag), -1);
+
+    // Encrypt without mentioning the unknown parameter.
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    string message = "12345678901234567890123456789012";
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+
+    // Decrypt including the unknown parameter.
+    auto decrypt_params = AuthorizationSetBuilder()
+                                  .BlockMode(BlockMode::ECB)
+                                  .Padding(PaddingMode::NONE)
+                                  .Authorization(unknown_param);
+    string plaintext = DecryptMessage(ciphertext, decrypt_params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongMode
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized mode is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongMode) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    EXPECT_EQ(
+            ErrorCode::INCOMPATIBLE_BLOCK_MODE,
+            Begin(KeyPurpose::ENCRYPT,
+                  AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE)));
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongPadding
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized padding is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    EXPECT_EQ(
+            ErrorCode::INCOMPATIBLE_PADDING_MODE,
+            Begin(KeyPurpose::ENCRYPT,
+                  AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7)));
+}
+
+/*
+ * EncryptionOperationsTest.AesInvalidParams
+ *
+ * Verifies that AES encryption fails in the correct way when an duplicate parameters are specified.
+ */
+TEST_P(EncryptionOperationsTest, AesInvalidParams) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Padding(PaddingMode::PKCS7)));
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    auto result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                                     .BlockMode(BlockMode::CBC)
+                                                     .BlockMode(BlockMode::ECB)
+                                                     .Padding(PaddingMode::NONE));
+    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_BLOCK_MODE ||
+                result == ErrorCode::UNSUPPORTED_BLOCK_MODE);
+
+    result = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                                .BlockMode(BlockMode::ECB)
+                                                .Padding(PaddingMode::NONE)
+                                                .Padding(PaddingMode::PKCS7));
+    EXPECT_TRUE(result == ErrorCode::INCOMPATIBLE_PADDING_MODE ||
+                result == ErrorCode::UNSUPPORTED_PADDING_MODE);
+}
+
+/*
+ * EncryptionOperationsTest.AesWrongPurpose
+ *
+ * Verifies that AES encryption fails in the correct way when an unauthorized purpose is
+ * specified.
+ */
+TEST_P(EncryptionOperationsTest, AesWrongPurpose) {
+    auto err = GenerateKey(AuthorizationSetBuilder()
+                                   .Authorization(TAG_NO_AUTH_REQUIRED)
+                                   .AesKey(128)
+                                   .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
+                                   .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                   .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                   .Padding(PaddingMode::NONE));
+    ASSERT_EQ(ErrorCode::OK, err) << "Got " << err;
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    err = Begin(KeyPurpose::DECRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+
+    CheckedDeleteKey();
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesKey(128)
+                                                 .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                 .Padding(PaddingMode::NONE)));
+
+    err = Begin(KeyPurpose::ENCRYPT, AuthorizationSetBuilder()
+                                             .BlockMode(BlockMode::GCM)
+                                             .Padding(PaddingMode::NONE)
+                                             .Authorization(TAG_MAC_LENGTH, 128));
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PURPOSE, err) << "Got " << err;
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbCbcNoPaddingWrongInputSize
+ *
+ * Verifies that AES encryption fails in the correct way when provided an input that is not a
+ * multiple of the block size and no padding is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbCbcNoPaddingWrongInputSize) {
+    for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .AesEncryptionKey(128)
+                                                     .Authorization(TAG_BLOCK_MODE, blockMode)
+                                                     .Padding(PaddingMode::NONE)));
+        // Message is slightly shorter than two blocks.
+        string message(16 * 2 - 1, 'a');
+
+        auto params = AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+        AuthorizationSet out_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+        string ciphertext;
+        EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, &ciphertext));
+        EXPECT_EQ(0U, ciphertext.size());
+
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7Padding
+ *
+ * Verifies that AES PKCS7 padding works for any message length.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    // Try various message lengths; all should work.
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        string ciphertext = EncryptMessage(message, params);
+        EXPECT_EQ(i + 16 - (i % 16), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, params);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbWrongPadding
+ *
+ * Verifies that AES enryption fails in the correct way when an unauthorized padding mode is
+ * specified.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbWrongPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    // Try various message lengths; all should fail
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesEcbPkcs7PaddingCorrupted
+ *
+ * Verifies that AES decryption fails in the correct way when the padding is corrupted.
+ */
+TEST_P(EncryptionOperationsTest, AesEcbPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::ECB)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+
+    string message = "a";
+    string ciphertext = EncryptMessage(message, params);
+    EXPECT_EQ(16U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+
+    for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+        ++ciphertext[ciphertext.size() / 2];
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+        string plaintext;
+        ErrorCode error = Finish(message, &plaintext);
+        if (error == ErrorCode::INVALID_INPUT_LENGTH) {
+            // This is the expected error, we can exit the test now.
+            return;
+        } else {
+            // Very small chance we got valid decryption, so try again.
+            ASSERT_EQ(error, ErrorCode::OK);
+        }
+    }
+    FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
+}
+
+vector<uint8_t> CopyIv(const AuthorizationSet& set) {
+    auto iv = set.GetTagValue(TAG_NONCE);
+    EXPECT_TRUE(iv);
+    return iv->get();
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrRoundTripSuccess
+ *
+ * Verifies that AES CTR mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+
+    string message = "123";
+    AuthorizationSet out_params;
+    string ciphertext1 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv1 = CopyIv(out_params);
+    EXPECT_EQ(16U, iv1.size());
+
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    out_params.Clear();
+    string ciphertext2 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv2 = CopyIv(out_params);
+    EXPECT_EQ(16U, iv2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    auto params_iv1 =
+            AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv1);
+    auto params_iv2 =
+            AuthorizationSetBuilder().Authorizations(params).Authorization(TAG_NONCE, iv2);
+
+    string plaintext = DecryptMessage(ciphertext1, params_iv1);
+    EXPECT_EQ(message, plaintext);
+    plaintext = DecryptMessage(ciphertext2, params_iv2);
+    EXPECT_EQ(message, plaintext);
+
+    // Using the wrong IV will result in a "valid" decryption, but the data will be garbage.
+    plaintext = DecryptMessage(ciphertext1, params_iv2);
+    EXPECT_NE(message, plaintext);
+    plaintext = DecryptMessage(ciphertext2, params_iv1);
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesIncremental
+ *
+ * Verifies that AES works, all modes, when provided data in various size increments.
+ */
+TEST_P(EncryptionOperationsTest, AesIncremental) {
+    auto block_modes = {
+            BlockMode::ECB,
+            BlockMode::CBC,
+            BlockMode::CTR,
+            BlockMode::GCM,
+    };
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(block_modes)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    for (int increment = 1; increment <= 240; ++increment) {
+        for (auto block_mode : block_modes) {
+            string message(240, 'a');
+            auto params =
+                    AuthorizationSetBuilder().BlockMode(block_mode).Padding(PaddingMode::NONE);
+            if (block_mode == BlockMode::GCM) {
+                params.Authorization(TAG_MAC_LENGTH, 128) /* for GCM */;
+            }
+
+            AuthorizationSet output_params;
+            EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &output_params));
+
+            string ciphertext;
+            string to_send;
+            for (size_t i = 0; i < message.size(); i += increment) {
+                EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext));
+            }
+            EXPECT_EQ(ErrorCode::OK, Finish(to_send, &ciphertext))
+                    << "Error sending " << to_send << " with block mode " << block_mode;
+
+            switch (block_mode) {
+                case BlockMode::GCM:
+                    EXPECT_EQ(message.size() + 16, ciphertext.size());
+                    break;
+                case BlockMode::CTR:
+                    EXPECT_EQ(message.size(), ciphertext.size());
+                    break;
+                case BlockMode::CBC:
+                case BlockMode::ECB:
+                    EXPECT_EQ(message.size() + message.size() % 16, ciphertext.size());
+                    break;
+            }
+
+            auto iv = output_params.GetTagValue(TAG_NONCE);
+            switch (block_mode) {
+                case BlockMode::CBC:
+                case BlockMode::GCM:
+                case BlockMode::CTR:
+                    ASSERT_TRUE(iv) << "No IV for block mode " << block_mode;
+                    EXPECT_EQ(block_mode == BlockMode::GCM ? 12U : 16U, iv->get().size());
+                    params.push_back(TAG_NONCE, iv->get());
+                    break;
+
+                case BlockMode::ECB:
+                    EXPECT_FALSE(iv) << "ECB mode should not generate IV";
+                    break;
+            }
+
+            EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params))
+                    << "Decrypt begin() failed for block mode " << block_mode;
+
+            string plaintext;
+            for (size_t i = 0; i < ciphertext.size(); i += increment) {
+                EXPECT_EQ(ErrorCode::OK, Update(ciphertext.substr(i, increment), &plaintext));
+            }
+            ErrorCode error = Finish(to_send, &plaintext);
+            ASSERT_EQ(ErrorCode::OK, error) << "Decryption failed for block mode " << block_mode
+                                            << " and increment " << increment;
+            if (error == ErrorCode::OK) {
+                ASSERT_EQ(message, plaintext) << "Decryption didn't match for block mode "
+                                              << block_mode << " and increment " << increment;
+            }
+        }
+    }
+}
+
+struct AesCtrSp80038aTestVector {
+    const char* key;
+    const char* nonce;
+    const char* plaintext;
+    const char* ciphertext;
+};
+
+// These test vectors are taken from
+// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf, section F.5.
+static const AesCtrSp80038aTestVector kAesCtrSp80038aTestVectors[] = {
+        // AES-128
+        {
+                "2b7e151628aed2a6abf7158809cf4f3c",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "874d6191b620e3261bef6864990db6ce9806f66b7970fdff8617187bb9fffdff"
+                "5ae4df3edbd5d35e5b4f09020db03eab1e031dda2fbe03d1792170a0f3009cee",
+        },
+        // AES-192
+        {
+                "8e73b0f7da0e6452c810f32b809079e562f8ead2522c6b7b",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "1abc932417521ca24f2b0459fe7e6e0b090339ec0aa6faefd5ccc2c6f4ce8e94"
+                "1e36b26bd1ebc670d1bd1d665620abf74f78a7f6d29809585a97daec58c6b050",
+        },
+        // AES-256
+        {
+                "603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4",
+                "f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff",
+                "6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e51"
+                "30c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710",
+                "601ec313775789a5b7a7f504bbf3d228f443e3ca4d62b59aca84e990cacaf5c5"
+                "2b0930daa23de94ce87017ba2d84988ddfc9c58db67aada613c2dd08457941a6",
+        },
+};
+
+/*
+ * EncryptionOperationsTest.AesCtrSp80038aTestVector
+ *
+ * Verifies AES CTR implementation against SP800-38A test vectors.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrSp80038aTestVector) {
+    std::vector<uint32_t> InvalidSizes = InvalidKeySizes(Algorithm::AES);
+    for (size_t i = 0; i < 3; i++) {
+        const AesCtrSp80038aTestVector& test(kAesCtrSp80038aTestVectors[i]);
+        const string key = hex2str(test.key);
+        if (std::find(InvalidSizes.begin(), InvalidSizes.end(), (key.size() * 8)) !=
+            InvalidSizes.end())
+            continue;
+        const string nonce = hex2str(test.nonce);
+        const string plaintext = hex2str(test.plaintext);
+        const string ciphertext = hex2str(test.ciphertext);
+        CheckAesCtrTestVector(key, nonce, plaintext, ciphertext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrIncompatiblePaddingMode
+ *
+ * Verifies that keymint rejects use of CTR mode with PKCS7 padding in the correct way.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrIncompatiblePaddingMode) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Padding(PaddingMode::PKCS7)));
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CTR).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCtrInvalidCallerNonce
+ *
+ * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_P(EncryptionOperationsTest, AesCtrInvalidCallerNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CTR)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::CTR)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_NONCE, AidlBuf(string(1, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CTR)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf(string(15, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CTR)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf(string(17, 'a')));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesCbcRoundTripSuccess
+ *
+ * Verifies that keymint fails correctly when the user supplies an incorrect-size nonce.
+ */
+TEST_P(EncryptionOperationsTest, AesCbcRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+    // Two-block message.
+    string message = "12345678901234567890123456789012";
+    auto params = AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext1 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv1 = CopyIv(out_params);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    out_params.Clear();
+
+    string ciphertext2 = EncryptMessage(message, params, &out_params);
+    vector<uint8_t> iv2 = CopyIv(out_params);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    params.push_back(TAG_NONCE, iv1);
+    string plaintext = DecryptMessage(ciphertext1, params);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonce
+ *
+ * Verifies that AES caller-provided nonces work correctly.
+ */
+TEST_P(EncryptionOperationsTest, AesCallerNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+
+    // Don't specify nonce, should get a random one.
+    AuthorizationSetBuilder params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+    EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
+
+    params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should also work.
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
+    out_params.Clear();
+    ciphertext = EncryptMessage(message, params, &out_params);
+
+    // Decrypt with correct nonce.
+    plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Try with wrong nonce.
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("aaaaaaaaaaaaaaaa"));
+    plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesCallerNonceProhibited
+ *
+ * Verifies that caller-provided nonces are not permitted when not specified in the key
+ * authorizations.
+ */
+TEST_P(EncryptionOperationsTest, AesCallerNonceProhibited) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::CBC)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+
+    // Don't specify nonce, should get a random one.
+    AuthorizationSetBuilder params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet out_params;
+    string ciphertext = EncryptMessage(message, params, &out_params);
+    EXPECT_EQ(message.size(), ciphertext.size());
+    EXPECT_EQ(16U, out_params.GetTagValue(TAG_NONCE)->get().size());
+
+    params.push_back(TAG_NONCE, out_params.GetTagValue(TAG_NONCE)->get());
+    string plaintext = DecryptMessage(ciphertext, params);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should fail
+    params = AuthorizationSetBuilder()
+                     .BlockMode(BlockMode::CBC)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_NONCE, AidlBuf("abcdefghijklmnop"));
+    out_params.Clear();
+    EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED, Begin(KeyPurpose::ENCRYPT, params, &out_params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmRoundTripSuccess
+ *
+ * Verifies that AES GCM mode works.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+            << "Begin encrypt";
+    string ciphertext;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+    ASSERT_EQ(ciphertext.length(), message.length() + 16);
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
+    EXPECT_EQ(message.length(), plaintext.length());
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmRoundTripWithDelaySuccess
+ *
+ * Verifies that AES GCM mode works, even when there's a long delay
+ * between operations.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmRoundTripWithDelaySuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params))
+            << "Begin encrypt";
+    string ciphertext;
+    AuthorizationSet update_out_params;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    sleep(5);
+    ASSERT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+
+    ASSERT_EQ(ciphertext.length(), message.length() + 16);
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    ASSERT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params)) << "Begin decrypt";
+    string plaintext;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    sleep(5);
+    ASSERT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
+    sleep(5);
+    EXPECT_EQ(ErrorCode::OK, Finish("", &plaintext));
+    EXPECT_EQ(message.length(), plaintext.length());
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmDifferentNonces
+ *
+ * Verifies that encrypting the same data with different nonces produces different outputs.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmDifferentNonces) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)
+                                                 .Authorization(TAG_CALLER_NONCE)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+    string nonce1 = "000000000000";
+    string nonce2 = "111111111111";
+    string nonce3 = "222222222222";
+
+    string ciphertext1 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce1));
+    string ciphertext2 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce2));
+    string ciphertext3 =
+            EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128, AidlBuf(nonce3));
+
+    ASSERT_NE(ciphertext1, ciphertext2);
+    ASSERT_NE(ciphertext1, ciphertext3);
+    ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmDifferentAutoNonces
+ *
+ * Verifies that encrypting the same data with KeyMint generated nonces produces different outputs.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmDifferentAutoNonces) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .Authorization(TAG_BLOCK_MODE, BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+
+    string ciphertext1 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+    string ciphertext2 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+    string ciphertext3 = EncryptMessage(message, BlockMode::GCM, PaddingMode::NONE, 128);
+
+    ASSERT_NE(ciphertext1, ciphertext2);
+    ASSERT_NE(ciphertext1, ciphertext3);
+    ASSERT_NE(ciphertext2, ciphertext3);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTag
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag length is specified.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTag) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    string message = "123456789012345678901234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 96);
+
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmTooShortTagOnDecrypt
+ *
+ * Verifies that AES GCM mode fails correctly when a too-short tag is provided to decryption.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmTooShortTagOnDecrypt) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+    string aad = "foobar";
+    string message = "123456789012345678901234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    EXPECT_EQ(1U, begin_out_params.size());
+    ASSERT_TRUE(begin_out_params.GetTagValue(TAG_NONCE));
+
+    AuthorizationSet finish_out_params;
+    string ciphertext;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+
+    params = AuthorizationSetBuilder()
+                     .Authorizations(begin_out_params)
+                     .BlockMode(BlockMode::GCM)
+                     .Padding(PaddingMode::NONE)
+                     .Authorization(TAG_MAC_LENGTH, 96);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::INVALID_MAC_LENGTH, Begin(KeyPurpose::DECRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptKey
+ *
+ * Verifies that AES GCM mode fails correctly when the decryption key is incorrect.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmCorruptKey) {
+    const uint8_t nonce_bytes[] = {
+            0xb7, 0x94, 0x37, 0xae, 0x08, 0xff, 0x35, 0x5d, 0x7d, 0x8a, 0x4d, 0x0f,
+    };
+    string nonce = make_string(nonce_bytes);
+    const uint8_t ciphertext_bytes[] = {
+            0xb3, 0xf6, 0x79, 0x9e, 0x8f, 0x93, 0x26, 0xf2, 0xdf, 0x1e, 0x80, 0xfc,
+            0xd2, 0xcb, 0x16, 0xd7, 0x8c, 0x9d, 0xc7, 0xcc, 0x14, 0xbb, 0x67, 0x78,
+            0x62, 0xdc, 0x6c, 0x63, 0x9b, 0x3a, 0x63, 0x38, 0xd2, 0x4b, 0x31, 0x2d,
+            0x39, 0x89, 0xe5, 0x92, 0x0b, 0x5d, 0xbf, 0xc9, 0x76, 0x76, 0x5e, 0xfb,
+            0xfe, 0x57, 0xbb, 0x38, 0x59, 0x40, 0xa7, 0xa4, 0x3b, 0xdf, 0x05, 0xbd,
+            0xda, 0xe3, 0xc9, 0xd6, 0xa2, 0xfb, 0xbd, 0xfc, 0xc0, 0xcb, 0xa0,
+    };
+    string ciphertext = make_string(ciphertext_bytes);
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128)
+                          .Authorization(TAG_NONCE, nonce.data(), nonce.size());
+
+    auto import_params = AuthorizationSetBuilder()
+                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                 .AesEncryptionKey(128)
+                                 .BlockMode(BlockMode::GCM)
+                                 .Padding(PaddingMode::NONE)
+                                 .Authorization(TAG_CALLER_NONCE)
+                                 .Authorization(TAG_MIN_MAC_LENGTH, 128);
+
+    // Import correct key and decrypt
+    const uint8_t key_bytes[] = {
+            0xba, 0x76, 0x35, 0x4f, 0x0a, 0xed, 0x6e, 0x8d,
+            0x91, 0xf4, 0x5c, 0x4f, 0xf5, 0xa0, 0x62, 0xdb,
+    };
+    string key = make_string(key_bytes);
+    ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+    string plaintext = DecryptMessage(ciphertext, params);
+    CheckedDeleteKey();
+
+    // Corrupt key and attempt to decrypt
+    key[0] = 0;
+    ASSERT_EQ(ErrorCode::OK, ImportKey(import_params, KeyFormat::RAW, key));
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+    CheckedDeleteKey();
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadNoData
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data, but no data to
+ * encrypt.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmAadNoData) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "1234567890123456";
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
+    EXPECT_TRUE(finish_out_params.empty());
+
+    // Grab nonce
+    params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    ASSERT_EQ(ErrorCode::OK, UpdateAad(aad));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
+
+    EXPECT_TRUE(finish_out_params.empty());
+
+    EXPECT_EQ("", plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmMultiPartAad
+ *
+ * Verifies that AES GCM mode works when provided additional authenticated data in multiple
+ * chunks.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmMultiPartAad) {
+    const size_t tag_bits = 128;
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "123456789012345678901234567890123456";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, tag_bits);
+    AuthorizationSet begin_out_params;
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+    // No data, AAD only.
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
+
+    // Expect 128-bit (16-byte) tag appended to ciphertext.
+    EXPECT_EQ(message.size() + (tag_bits / 8), ciphertext.size());
+
+    // Grab nonce.
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foofoo"));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::OK, Finish(ciphertext, &plaintext));
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmAadOutOfOrder
+ *
+ * Verifies that AES GCM mode fails correctly when given AAD after data to encipher.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmAadOutOfOrder) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "123456789012345678901234567890123456";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+    AuthorizationSet begin_out_params;
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foo"));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::OK, Update(message, &ciphertext));
+    EXPECT_EQ(ErrorCode::INVALID_TAG, UpdateAad("foo"));
+
+    // The failure should have already cancelled the operation.
+    EXPECT_EQ(ErrorCode::INVALID_OPERATION_HANDLE, Abort());
+
+    op_ = {};
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmBadAad
+ *
+ * Verifies that AES GCM decryption fails correctly when additional authenticated date is wrong.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmBadAad) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "12345678901234567890123456789012";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+
+    // Grab nonce
+    begin_params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("barfoo"));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmWrongNonce
+ *
+ * Verifies that AES GCM decryption fails correctly when the nonce is incorrect.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmWrongNonce) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string message = "12345678901234567890123456789012";
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::GCM)
+                                .Padding(PaddingMode::NONE)
+                                .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
+    string ciphertext;
+    AuthorizationSet finish_out_params;
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+
+    // Wrong nonce
+    begin_params.push_back(TAG_NONCE, AidlBuf("123456789012"));
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad("foobar"));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+
+    // With wrong nonce, should have gotten garbage plaintext (or none).
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.AesGcmCorruptTag
+ *
+ * Verifies that AES GCM decryption fails correctly when the tag is wrong.
+ */
+TEST_P(EncryptionOperationsTest, AesGcmCorruptTag) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::GCM)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 128)));
+
+    string aad = "1234567890123456";
+    string message = "123456789012345678901234567890123456";
+
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::GCM)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_MAC_LENGTH, 128);
+
+    // Encrypt
+    AuthorizationSet begin_out_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params, &begin_out_params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
+    string ciphertext;
+    EXPECT_EQ(ErrorCode::OK, Finish(message, &ciphertext));
+
+    // Corrupt tag
+    ++(*ciphertext.rbegin());
+
+    // Grab nonce
+    params.push_back(begin_out_params);
+
+    // Decrypt.
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, params));
+    EXPECT_EQ(ErrorCode::OK, UpdateAad(aad));
+    string plaintext;
+    EXPECT_EQ(ErrorCode::VERIFICATION_FAILED, Finish(ciphertext, &plaintext));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbRoundTripSuccess
+ *
+ * Verifies that 3DES is basically functional.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbRoundTripSuccess) {
+    auto auths = AuthorizationSetBuilder()
+                         .TripleDesEncryptionKey(168)
+                         .BlockMode(BlockMode::ECB)
+                         .Authorization(TAG_NO_AUTH_REQUIRED)
+                         .Padding(PaddingMode::NONE);
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(auths));
+    // Two-block message.
+    string message = "1234567890123456";
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    string ciphertext1 = EncryptMessage(message, inParams);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    string ciphertext2 = EncryptMessage(string(message), inParams);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // ECB is deterministic.
+    EXPECT_EQ(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, inParams);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbNotAuthorized
+ *
+ * Verifies that CBC keys reject ECB usage.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbNotAuthorized) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbPkcs7Padding
+ *
+ * Tests ECB mode with PKCS#7 padding, various message sizes.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        auto inParams =
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+        string ciphertext = EncryptMessage(message, inParams);
+        EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, inParams);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbNoPaddingKeyWithPkcs7Padding
+ *
+ * Verifies that keys configured for no padding reject PKCS7 padding
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbNoPaddingKeyWithPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    auto inParams = AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::PKCS7);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, inParams));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbPkcs7PaddingCorrupted
+ *
+ * Verifies that corrupted padding is detected.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    string message = "a";
+    string ciphertext = EncryptMessage(message, BlockMode::ECB, PaddingMode::PKCS7);
+    EXPECT_EQ(8U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+
+    AuthorizationSetBuilder begin_params;
+    begin_params.push_back(TAG_BLOCK_MODE, BlockMode::ECB);
+    begin_params.push_back(TAG_PADDING, PaddingMode::PKCS7);
+
+    for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+        ++ciphertext[ciphertext.size() / 2];
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+        string plaintext;
+        EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
+        ErrorCode error = Finish(&plaintext);
+        if (error == ErrorCode::INVALID_ARGUMENT) {
+            // This is the expected error, we can exit the test now.
+            return;
+        } else {
+            // Very small chance we got valid decryption, so try again.
+            ASSERT_EQ(error, ErrorCode::OK);
+        }
+    }
+    FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
+}
+
+struct TripleDesTestVector {
+    const char* name;
+    const KeyPurpose purpose;
+    const BlockMode block_mode;
+    const PaddingMode padding_mode;
+    const char* key;
+    const char* iv;
+    const char* input;
+    const char* output;
+};
+
+// These test vectors are from NIST CAVP, plus a few custom variants to test padding, since all
+// of the NIST vectors are multiples of the block size.
+static const TripleDesTestVector kTripleDesTestVectors[] = {
+        {
+                "TECBMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "a2b5bc67da13dc92cd9d344aa238544a0e1fa79ef76810cd",  // key
+                "",                                                  // IV
+                "329d86bdf1bc5af4",                                  // input
+                "d946c2756d78633f",                                  // output
+        },
+        {
+                "TECBMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "49e692290d2a5e46bace79b9648a4c5d491004c262dc9d49",  // key
+                "",                                                  // IV
+                "6b1540781b01ce1997adae102dbf3c5b",                  // input
+                "4d0dc182d6e481ac4a3dc6ab6976ccae",                  // output
+        },
+        {
+                "TECBMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "52daec2ac7dc1958377392682f37860b2cc1ea2304bab0e9",  // key
+                "",                                                  // IV
+                "6daad94ce08acfe7",                                  // input
+                "660e7d32dcc90e79",                                  // output
+        },
+        {
+                "TECBMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::ECB, PaddingMode::NONE,
+                "7f8fe3d3f4a48394fb682c2919926d6ddfce8932529229ce",  // key
+                "",                                                  // IV
+                "e9653a0a1f05d31b9acd12d73aa9879d",                  // input
+                "9b2ae9d998efe62f1b592e7e1df8ff38",                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 0", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "b5cb1504802326c73df186e3e352a20de643b0d63ee30e37",  // key
+                "43f791134c5647ba",                                  // IV
+                "dcc153cef81d6f24",                                  // input
+                "92538bd8af18d3ba",                                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1", KeyPurpose::ENCRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "c689aee38a301bb316da75db36f110b5",                  // input
+                "e9afaba5ec75ea1bbe65506655bb4ecb",                  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1 PKCS7 variant", KeyPurpose::ENCRYPT, BlockMode::CBC,
+                PaddingMode::PKCS7,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "c689aee38a301bb316da75db36f110b500",                // input
+                "e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156",  // output
+        },
+        {
+                "TCBCMMT3 Encrypt 1 PKCS7 decrypted", KeyPurpose::DECRYPT, BlockMode::CBC,
+                PaddingMode::PKCS7,
+                "a49d7564199e97cb529d2c9d97bf2f98d35edf57ba1f7358",  // key
+                "c2e999cb6249023c",                                  // IV
+                "e9afaba5ec75ea1bbe65506655bb4ecb825aa27ec0656156",  // input
+                "c689aee38a301bb316da75db36f110b500",                // output
+        },
+        {
+                "TCBCMMT3 Decrypt 0", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "5eb6040d46082c7aa7d06dfd08dfeac8c18364c1548c3ba1",  // key
+                "41746c7e442d3681",                                  // IV
+                "c53a7b0ec40600fe",                                  // input
+                "d4f00eb455de1034",                                  // output
+        },
+        {
+                "TCBCMMT3 Decrypt 1", KeyPurpose::DECRYPT, BlockMode::CBC, PaddingMode::NONE,
+                "5b1cce7c0dc1ec49130dfb4af45785ab9179e567f2c7d549",  // key
+                "3982bc02c3727d45",                                  // IV
+                "6006f10adef52991fcc777a1238bbb65",                  // input
+                "edae09288e9e3bc05746d872b48e3b29",                  // output
+        },
+};
+
+/*
+ * EncryptionOperationsTest.TripleDesTestVector
+ *
+ * Verifies that NIST (plus a few extra) test vectors produce the correct results.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesTestVector) {
+    constexpr size_t num_tests = sizeof(kTripleDesTestVectors) / sizeof(TripleDesTestVector);
+    for (auto* test = kTripleDesTestVectors; test < kTripleDesTestVectors + num_tests; ++test) {
+        SCOPED_TRACE(test->name);
+        CheckTripleDesTestVector(test->purpose, test->block_mode, test->padding_mode,
+                                 hex2str(test->key), hex2str(test->iv), hex2str(test->input),
+                                 hex2str(test->output));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcRoundTripSuccess
+ *
+ * Validates CBC mode functionality.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcRoundTripSuccess) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    ASSERT_GT(key_blob_.size(), 0U);
+
+    // Two-block message.
+    string message = "1234567890123456";
+    vector<uint8_t> iv1;
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv1);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+
+    vector<uint8_t> iv2;
+    string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv2);
+    EXPECT_EQ(message.size(), ciphertext2.size());
+
+    // IVs should be random, so ciphertexts should differ.
+    EXPECT_NE(iv1, iv2);
+    EXPECT_NE(ciphertext1, ciphertext2);
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv1);
+    EXPECT_EQ(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesInvalidCallerIv
+ *
+ * Validates that keymint fails correctly when the user supplies an incorrect-size IV.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesInvalidCallerIv) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+    auto params = AuthorizationSetBuilder()
+                          .BlockMode(BlockMode::CBC)
+                          .Padding(PaddingMode::NONE)
+                          .Authorization(TAG_NONCE, AidlBuf("abcdefg"));
+    EXPECT_EQ(ErrorCode::INVALID_NONCE, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCallerIv
+ *
+ * Validates that 3DES keys can allow caller-specified IVs, and use them correctly.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCallerIv) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_CALLER_NONCE)
+                                                 .Padding(PaddingMode::NONE)));
+    string message = "1234567890123456";
+    vector<uint8_t> iv;
+    // Don't specify IV, should get a random one.
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+    EXPECT_EQ(8U, iv.size());
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify an IV, should also work.
+    iv = AidlBuf("abcdefgh");
+    string ciphertext2 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, iv);
+
+    // Decrypt with correct IV.
+    plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now try with wrong IV.
+    plaintext = DecryptMessage(ciphertext2, BlockMode::CBC, PaddingMode::NONE, AidlBuf("aaaaaaaa"));
+    EXPECT_NE(message, plaintext);
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCallerNonceProhibited.
+ *
+ * Verifies that 3DES keys without TAG_CALLER_NONCE do not allow caller-specified IVs.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCallerNonceProhibited) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    string message = "12345678901234567890123456789012";
+    vector<uint8_t> iv;
+    // Don't specify nonce, should get a random one.
+    string ciphertext1 = EncryptMessage(message, BlockMode::CBC, PaddingMode::NONE, &iv);
+    EXPECT_EQ(message.size(), ciphertext1.size());
+    EXPECT_EQ(8U, iv.size());
+
+    string plaintext = DecryptMessage(ciphertext1, BlockMode::CBC, PaddingMode::NONE, iv);
+    EXPECT_EQ(message, plaintext);
+
+    // Now specify a nonce, should fail.
+    auto input_params = AuthorizationSetBuilder()
+                                .Authorization(TAG_NONCE, AidlBuf("abcdefgh"))
+                                .BlockMode(BlockMode::CBC)
+                                .Padding(PaddingMode::NONE);
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::CALLER_NONCE_PROHIBITED,
+              Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcNotAuthorized
+ *
+ * Verifies that 3DES ECB-only keys do not allow CBC usage.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcNotAuthorized) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+    // Two-block message.
+    string message = "1234567890123456";
+    auto begin_params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    EXPECT_EQ(ErrorCode::INCOMPATIBLE_BLOCK_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesEcbCbcNoPaddingWrongInputSize
+ *
+ * Verifies that unpadded CBC operations reject inputs that are not a multiple of block size.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesEcbCbcNoPaddingWrongInputSize) {
+    for (BlockMode blockMode : {BlockMode::ECB, BlockMode::CBC}) {
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .TripleDesEncryptionKey(168)
+                                                     .BlockMode(blockMode)
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .Padding(PaddingMode::NONE)));
+        // Message is slightly shorter than two blocks.
+        string message = "123456789012345";
+
+        auto begin_params =
+                AuthorizationSetBuilder().BlockMode(blockMode).Padding(PaddingMode::NONE);
+        AuthorizationSet output_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, begin_params, &output_params));
+        string ciphertext;
+        EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, Finish(message, "", &ciphertext));
+
+        CheckedDeleteKey();
+    }
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCbcPkcs7Padding.
+ *
+ * Verifies that PKCS7 padding works correctly in CBC mode.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    // Try various message lengths; all should work.
+    for (size_t i = 0; i < 32; ++i) {
+        string message(i, 'a');
+        vector<uint8_t> iv;
+        string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
+        EXPECT_EQ(i + 8 - (i % 8), ciphertext.size());
+        string plaintext = DecryptMessage(ciphertext, BlockMode::CBC, PaddingMode::PKCS7, iv);
+        EXPECT_EQ(message, plaintext);
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcNoPaddingKeyWithPkcs7Padding
+ *
+ * Verifies that a key that requires PKCS7 padding cannot be used in unpadded mode.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcNoPaddingKeyWithPkcs7Padding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    // Try various message lengths; all should fail.
+    for (size_t i = 0; i < 32; ++i) {
+        auto begin_params =
+                AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::PKCS7);
+        EXPECT_EQ(ErrorCode::INCOMPATIBLE_PADDING_MODE, Begin(KeyPurpose::ENCRYPT, begin_params));
+    }
+}
+
+/*
+ * EncryptionOperationsTest.TripleDesCbcPkcs7PaddingCorrupted
+ *
+ * Verifies that corrupted PKCS7 padding is rejected during decryption.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcPkcs7PaddingCorrupted) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::PKCS7)));
+
+    string message = "a";
+    vector<uint8_t> iv;
+    string ciphertext = EncryptMessage(message, BlockMode::CBC, PaddingMode::PKCS7, &iv);
+    EXPECT_EQ(8U, ciphertext.size());
+    EXPECT_NE(ciphertext, message);
+
+    auto begin_params = AuthorizationSetBuilder()
+                                .BlockMode(BlockMode::CBC)
+                                .Padding(PaddingMode::PKCS7)
+                                .Authorization(TAG_NONCE, iv);
+
+    for (size_t i = 0; i < kMaxPaddingCorruptionRetries; ++i) {
+        ++ciphertext[ciphertext.size() / 2];
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, begin_params));
+        string plaintext;
+        EXPECT_EQ(ErrorCode::OK, Update(ciphertext, &plaintext));
+        ErrorCode error = Finish(&plaintext);
+        if (error == ErrorCode::INVALID_ARGUMENT) {
+            // This is the expected error, we can exit the test now.
+            return;
+        } else {
+            // Very small chance we got valid decryption, so try again.
+            ASSERT_EQ(error, ErrorCode::OK);
+        }
+    }
+    FAIL() << "Corrupt ciphertext should have failed to decrypt by now.";
+}
+
+/*
+ * EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding.
+ *
+ * Verifies that 3DES CBC works with many different input sizes.
+ */
+TEST_P(EncryptionOperationsTest, TripleDesCbcIncrementalNoPadding) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .TripleDesEncryptionKey(168)
+                                                 .BlockMode(BlockMode::CBC)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Padding(PaddingMode::NONE)));
+
+    int increment = 7;
+    string message(240, 'a');
+    AuthorizationSet input_params =
+            AuthorizationSetBuilder().BlockMode(BlockMode::CBC).Padding(PaddingMode::NONE);
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, input_params, &output_params));
+
+    string ciphertext;
+    for (size_t i = 0; i < message.size(); i += increment)
+        EXPECT_EQ(ErrorCode::OK, Update(message.substr(i, increment), &ciphertext));
+    EXPECT_EQ(ErrorCode::OK, Finish(&ciphertext));
+    EXPECT_EQ(message.size(), ciphertext.size());
+
+    // Move TAG_NONCE into input_params
+    input_params = output_params;
+    input_params.push_back(TAG_BLOCK_MODE, BlockMode::CBC);
+    input_params.push_back(TAG_PADDING, PaddingMode::NONE);
+    output_params.Clear();
+
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, input_params, &output_params));
+    string plaintext;
+    for (size_t i = 0; i < ciphertext.size(); i += increment)
+        EXPECT_EQ(ErrorCode::OK, Update(ciphertext.substr(i, increment), &plaintext));
+    EXPECT_EQ(ErrorCode::OK, Finish(&plaintext));
+    EXPECT_EQ(ciphertext.size(), plaintext.size());
+    EXPECT_EQ(message, plaintext);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(EncryptionOperationsTest);
+
+typedef KeyMintAidlTestBase MaxOperationsTest;
+
+/*
+ * MaxOperationsTest.TestLimitAes
+ *
+ * Verifies that the max uses per boot tag works correctly with AES keys.
+ */
+TEST_P(MaxOperationsTest, TestLimitAes) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .EcbMode()
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)));
+
+    string message = "1234567890123456";
+
+    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
+
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+
+    // Fourth time should fail.
+    EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::ENCRYPT, params));
+}
+
+/*
+ * MaxOperationsTest.TestLimitRsa
+ *
+ * Verifies that the max uses per boot tag works correctly with RSA keys.
+ */
+TEST_P(MaxOperationsTest, TestLimitRsa) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .NoDigestOrPadding()
+                                                 .Authorization(TAG_MAX_USES_PER_BOOT, 3)
+                                                 .SetDefaultValidity()));
+
+    string message = "1234567890123456";
+
+    auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+    SignMessage(message, params);
+    SignMessage(message, params);
+    SignMessage(message, params);
+
+    // Fourth time should fail.
+    EXPECT_EQ(ErrorCode::KEY_MAX_OPS_EXCEEDED, Begin(KeyPurpose::SIGN, params));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(MaxOperationsTest);
+
+typedef KeyMintAidlTestBase UsageCountLimitTest;
+
+/*
+ * UsageCountLimitTest.TestSingleUseAes
+ *
+ * Verifies that the usage count limit tag = 1 works correctly with AES keys.
+ */
+TEST_P(UsageCountLimitTest, TestSingleUseAes) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .EcbMode()
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 1)));
+
+    // Check the usage count limit tag appears in the authorizations.
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics_) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+            << "key usage count limit " << 1U << " missing";
+
+    string message = "1234567890123456";
+    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
+
+    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+    AuthorizationSet keystore_auths =
+            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
+
+    // First usage of AES key should work.
+    EncryptMessage(message, params);
+
+    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
+        // Usage count limit tag is enforced by hardware. After using the key, the key blob
+        // must be invalidated from secure storage (such as RPMB partition).
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
+    } else {
+        // Usage count limit tag is enforced by keystore, keymint does nothing.
+        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+    }
+}
+
+/*
+ * UsageCountLimitTest.TestLimitedUseAes
+ *
+ * Verifies that the usage count limit tag > 1 works correctly with AES keys.
+ */
+TEST_P(UsageCountLimitTest, TestLimitedUseAes) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .EcbMode()
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 3)));
+
+    // Check the usage count limit tag appears in the authorizations.
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics_) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
+            << "key usage count limit " << 3U << " missing";
+
+    string message = "1234567890123456";
+    auto params = AuthorizationSetBuilder().EcbMode().Padding(PaddingMode::NONE);
+
+    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+    AuthorizationSet keystore_auths =
+            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
+
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+    EncryptMessage(message, params);
+
+    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
+        // Usage count limit tag is enforced by hardware. After using the key, the key blob
+        // must be invalidated from secure storage (such as RPMB partition).
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::ENCRYPT, params));
+    } else {
+        // Usage count limit tag is enforced by keystore, keymint does nothing.
+        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, params));
+    }
+}
+
+/*
+ * UsageCountLimitTest.TestSingleUseRsa
+ *
+ * Verifies that the usage count limit tag = 1 works correctly with RSA keys.
+ */
+TEST_P(UsageCountLimitTest, TestSingleUseRsa) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .NoDigestOrPadding()
+                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                 .SetDefaultValidity()));
+
+    // Check the usage count limit tag appears in the authorizations.
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics_) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+            << "key usage count limit " << 1U << " missing";
+
+    string message = "1234567890123456";
+    auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+    AuthorizationSet keystore_auths =
+            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
+
+    // First usage of RSA key should work.
+    SignMessage(message, params);
+
+    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U)) {
+        // Usage count limit tag is enforced by hardware. After using the key, the key blob
+        // must be invalidated from secure storage (such as RPMB partition).
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
+    } else {
+        // Usage count limit tag is enforced by keystore, keymint does nothing.
+        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U));
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
+    }
+}
+
+/*
+ * UsageCountLimitTest.TestLimitUseRsa
+ *
+ * Verifies that the usage count limit tag > 1 works correctly with RSA keys.
+ */
+TEST_P(UsageCountLimitTest, TestLimitUseRsa) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaSigningKey(1024, 65537)
+                                                 .NoDigestOrPadding()
+                                                 .Authorization(TAG_USAGE_COUNT_LIMIT, 3)
+                                                 .SetDefaultValidity()));
+
+    // Check the usage count limit tag appears in the authorizations.
+    AuthorizationSet auths;
+    for (auto& entry : key_characteristics_) {
+        auths.push_back(AuthorizationSet(entry.authorizations));
+    }
+    EXPECT_TRUE(auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U))
+            << "key usage count limit " << 3U << " missing";
+
+    string message = "1234567890123456";
+    auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+    AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+    AuthorizationSet keystore_auths =
+            SecLevelAuthorizations(key_characteristics_, SecurityLevel::KEYSTORE);
+
+    SignMessage(message, params);
+    SignMessage(message, params);
+    SignMessage(message, params);
+
+    if (hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U)) {
+        // Usage count limit tag is enforced by hardware. After using the key, the key blob
+        // must be invalidated from secure storage (such as RPMB partition).
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
+    } else {
+        // Usage count limit tag is enforced by keystore, keymint does nothing.
+        EXPECT_TRUE(keystore_auths.Contains(TAG_USAGE_COUNT_LIMIT, 3U));
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::SIGN, params));
+    }
+}
+
+/*
+ * UsageCountLimitTest.TestSingleUseKeyAndRollbackResistance
+ *
+ * Verifies that when rollback resistance is supported by the KeyMint implementation with
+ * the secure hardware, the single use key with usage count limit tag = 1 must also be enforced
+ * in hardware.
+ */
+TEST_P(UsageCountLimitTest, TestSingleUseKeyAndRollbackResistance) {
+    if (SecLevel() == SecurityLevel::STRONGBOX) return;
+
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE)
+                                     .SetDefaultValidity());
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    if (error == ErrorCode::OK) {
+        // Rollback resistance is supported by KeyMint, verify it is enforced in hardware.
+        AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+        ASSERT_EQ(ErrorCode::OK, DeleteKey());
+
+        // The KeyMint should also enforce single use key in hardware when it supports rollback
+        // resistance.
+        ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                     .RsaSigningKey(1024, 65537)
+                                                     .NoDigestOrPadding()
+                                                     .Authorization(TAG_USAGE_COUNT_LIMIT, 1)
+                                                     .SetDefaultValidity()));
+
+        // Check the usage count limit tag appears in the hardware authorizations.
+        AuthorizationSet hardware_auths = HwEnforcedAuthorizations(key_characteristics_);
+        EXPECT_TRUE(hardware_auths.Contains(TAG_USAGE_COUNT_LIMIT, 1U))
+                << "key usage count limit " << 1U << " missing";
+
+        string message = "1234567890123456";
+        auto params = AuthorizationSetBuilder().NoDigestOrPadding();
+
+        // First usage of RSA key should work.
+        SignMessage(message, params);
+
+        // Usage count limit tag is enforced by hardware. After using the key, the key blob
+        // must be invalidated from secure storage (such as RPMB partition).
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB, Begin(KeyPurpose::SIGN, params));
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(UsageCountLimitTest);
+
+typedef KeyMintAidlTestBase GetHardwareInfoTest;
+
+TEST_P(GetHardwareInfoTest, GetHardwareInfo) {
+    // Retrieving hardware info should give the same result each time.
+    KeyMintHardwareInfo info;
+    ASSERT_TRUE(keyMint().getHardwareInfo(&info).isOk());
+    KeyMintHardwareInfo info2;
+    ASSERT_TRUE(keyMint().getHardwareInfo(&info2).isOk());
+    EXPECT_EQ(info, info2);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(GetHardwareInfoTest);
+
+typedef KeyMintAidlTestBase AddEntropyTest;
+
+/*
+ * AddEntropyTest.AddEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up.  There's no way to test that entropy
+ * is actually added.
+ */
+TEST_P(AddEntropyTest, AddEntropy) {
+    string data = "foo";
+    EXPECT_TRUE(keyMint().addRngEntropy(vector<uint8_t>(data.begin(), data.end())).isOk());
+}
+
+/*
+ * AddEntropyTest.AddEmptyEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given an empty buffer.
+ */
+TEST_P(AddEntropyTest, AddEmptyEntropy) {
+    EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf()).isOk());
+}
+
+/*
+ * AddEntropyTest.AddLargeEntropy
+ *
+ * Verifies that the addRngEntropy method doesn't blow up when given a largish amount of data.
+ */
+TEST_P(AddEntropyTest, AddLargeEntropy) {
+    EXPECT_TRUE(keyMint().addRngEntropy(AidlBuf(string(2 * 1024, 'a'))).isOk());
+}
+
+/*
+ * AddEntropyTest.AddTooLargeEntropy
+ *
+ * Verifies that the addRngEntropy method rejects more than 2KiB  of data.
+ */
+TEST_P(AddEntropyTest, AddTooLargeEntropy) {
+    ErrorCode rc = GetReturnErrorCode(keyMint().addRngEntropy(AidlBuf(string(2 * 1024 + 1, 'a'))));
+    EXPECT_EQ(ErrorCode::INVALID_INPUT_LENGTH, rc);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(AddEntropyTest);
+
+typedef KeyMintAidlTestBase KeyDeletionTest;
+
+/**
+ * KeyDeletionTest.DeleteKey
+ *
+ * This test checks that if rollback protection is implemented, DeleteKey invalidates a formerly
+ * valid key blob.
+ */
+TEST_P(KeyDeletionTest, DeleteKey) {
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE)
+                                     .SetDefaultValidity());
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        ASSERT_EQ(ErrorCode::OK, DeleteKey(true /* keep key blob */));
+
+        string message = "12345678901234567890123456789012";
+        AuthorizationSet begin_out_params;
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+                  Begin(KeyPurpose::SIGN, key_blob_,
+                        AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
+                        &begin_out_params));
+        AbortIfNeeded();
+        key_blob_ = AidlBuf();
+    }
+}
+
+/**
+ * KeyDeletionTest.DeleteInvalidKey
+ *
+ * This test checks that the HAL excepts invalid key blobs..
+ */
+TEST_P(KeyDeletionTest, DeleteInvalidKey) {
+    // Generate key just to check if rollback protection is implemented
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE)
+                                     .SetDefaultValidity());
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet enforced(SecLevelAuthorizations());
+        ASSERT_TRUE(enforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        // Delete the key we don't care about the result at this point.
+        DeleteKey();
+
+        // Now create an invalid key blob and delete it.
+        key_blob_ = AidlBuf("just some garbage data which is not a valid key blob");
+
+        ASSERT_EQ(ErrorCode::OK, DeleteKey());
+    }
+}
+
+/**
+ * KeyDeletionTest.DeleteAllKeys
+ *
+ * This test is disarmed by default. To arm it use --arm_deleteAllKeys.
+ *
+ * BEWARE: This test has serious side effects. All user keys will be lost! This includes
+ * FBE/FDE encryption keys, which means that the device will not even boot until after the
+ * device has been wiped manually (e.g., fastboot flashall -w), and new FBE/FDE keys have
+ * been provisioned. Use this test only on dedicated testing devices that have no valuable
+ * credentials stored in Keystore/Keymint.
+ */
+TEST_P(KeyDeletionTest, DeleteAllKeys) {
+    if (!arm_deleteAllKeys) return;
+    auto error = GenerateKey(AuthorizationSetBuilder()
+                                     .RsaSigningKey(2048, 65537)
+                                     .Digest(Digest::NONE)
+                                     .Padding(PaddingMode::NONE)
+                                     .Authorization(TAG_NO_AUTH_REQUIRED)
+                                     .Authorization(TAG_ROLLBACK_RESISTANCE));
+    ASSERT_TRUE(error == ErrorCode::ROLLBACK_RESISTANCE_UNAVAILABLE || error == ErrorCode::OK);
+
+    // Delete must work if rollback protection is implemented
+    if (error == ErrorCode::OK) {
+        AuthorizationSet hardwareEnforced(SecLevelAuthorizations());
+        ASSERT_TRUE(hardwareEnforced.Contains(TAG_ROLLBACK_RESISTANCE));
+
+        ASSERT_EQ(ErrorCode::OK, DeleteAllKeys());
+
+        string message = "12345678901234567890123456789012";
+        AuthorizationSet begin_out_params;
+
+        EXPECT_EQ(ErrorCode::INVALID_KEY_BLOB,
+                  Begin(KeyPurpose::SIGN, key_blob_,
+                        AuthorizationSetBuilder().Digest(Digest::NONE).Padding(PaddingMode::NONE),
+                        &begin_out_params));
+        AbortIfNeeded();
+        key_blob_ = AidlBuf();
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyDeletionTest);
+
+typedef KeyMintAidlTestBase KeyUpgradeTest;
+
+/**
+ * KeyUpgradeTest.UpgradeInvalidKey
+ *
+ * This test checks that the HAL excepts invalid key blobs..
+ */
+TEST_P(KeyUpgradeTest, UpgradeInvalidKey) {
+    AidlBuf key_blob = AidlBuf("just some garbage data which is not a valid key blob");
+
+    std::vector<uint8_t> new_blob;
+    Status result = keymint_->upgradeKey(key_blob,
+                                         AuthorizationSetBuilder()
+                                                 .Authorization(TAG_APPLICATION_ID, "clientid")
+                                                 .Authorization(TAG_APPLICATION_DATA, "appdata")
+                                                 .vector_data(),
+                                         &new_blob);
+    ASSERT_EQ(ErrorCode::INVALID_KEY_BLOB, GetReturnErrorCode(result));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyUpgradeTest);
+
+using UpgradeKeyTest = KeyMintAidlTestBase;
+
+/*
+ * UpgradeKeyTest.UpgradeKey
+ *
+ * Verifies that calling upgrade key on an up-to-date key works (i.e. does nothing).
+ */
+TEST_P(UpgradeKeyTest, UpgradeKey) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .AesEncryptionKey(128)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)));
+
+    auto result = UpgradeKey(key_blob_);
+
+    // Key doesn't need upgrading.  Should get okay, but no new key blob.
+    EXPECT_EQ(result, std::make_pair(ErrorCode::OK, vector<uint8_t>()));
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(UpgradeKeyTest);
+
+using ClearOperationsTest = KeyMintAidlTestBase;
+
+/*
+ * ClearSlotsTest.TooManyOperations
+ *
+ * Verifies that TOO_MANY_OPERATIONS is returned after the max number of
+ * operations are started without being finished or aborted. Also verifies
+ * that aborting the operations clears the operations.
+ *
+ */
+TEST_P(ClearOperationsTest, TooManyOperations) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .RsaEncryptionKey(2048, 65537)
+                                                 .Padding(PaddingMode::NONE)
+                                                 .SetDefaultValidity()));
+
+    auto params = AuthorizationSetBuilder().Padding(PaddingMode::NONE);
+    constexpr size_t max_operations = 100;  // set to arbituary large number
+    std::shared_ptr<IKeyMintOperation> op_handles[max_operations];
+    AuthorizationSet out_params;
+    ErrorCode result;
+    size_t i;
+
+    for (i = 0; i < max_operations; i++) {
+        result = Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params, op_handles[i]);
+        if (ErrorCode::OK != result) {
+            break;
+        }
+    }
+    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS, result);
+    // Try again just in case there's a weird overflow bug
+    EXPECT_EQ(ErrorCode::TOO_MANY_OPERATIONS,
+              Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params));
+    for (size_t j = 0; j < i; j++) {
+        EXPECT_EQ(ErrorCode::OK, Abort(op_handles[j]))
+                << "Aboort failed for i = " << j << std::endl;
+    }
+    EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, key_blob_, params, &out_params));
+    AbortIfNeeded();
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(ClearOperationsTest);
+
+typedef KeyMintAidlTestBase TransportLimitTest;
+
+/*
+ * TransportLimitTest.LargeFinishInput
+ *
+ * Verifies that passing input data to finish succeeds as expected.
+ */
+TEST_P(TransportLimitTest, LargeFinishInput) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .AesEncryptionKey(128)
+                                                 .BlockMode(BlockMode::ECB)
+                                                 .Padding(PaddingMode::NONE)));
+
+    for (int msg_size = 8 /* 256 bytes */; msg_size <= 11 /* 2 KiB */; msg_size++) {
+        auto cipher_params =
+                AuthorizationSetBuilder().BlockMode(BlockMode::ECB).Padding(PaddingMode::NONE);
+
+        AuthorizationSet out_params;
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::ENCRYPT, cipher_params, &out_params));
+
+        string plain_message = std::string(1 << msg_size, 'x');
+        string encrypted_message;
+        auto rc = Finish(plain_message, &encrypted_message);
+
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), encrypted_message.size())
+                << "Encrypt finish returned OK, but did not consume all of the given input";
+        cipher_params.push_back(out_params);
+
+        EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::DECRYPT, cipher_params));
+
+        string decrypted_message;
+        rc = Finish(encrypted_message, &decrypted_message);
+        EXPECT_EQ(ErrorCode::OK, rc);
+        EXPECT_EQ(plain_message.size(), decrypted_message.size())
+                << "Decrypt finish returned OK, did not consume all of the given input";
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(TransportLimitTest);
+
+typedef KeyMintAidlTestBase KeyAgreementTest;
+
+int CurveToOpenSslCurveName(EcCurve curve) {
+    switch (curve) {
+        case EcCurve::P_224:
+            return NID_secp224r1;
+        case EcCurve::P_256:
+            return NID_X9_62_prime256v1;
+        case EcCurve::P_384:
+            return NID_secp384r1;
+        case EcCurve::P_521:
+            return NID_secp521r1;
+    }
+}
+
+/*
+ * KeyAgreementTest.Ecdh
+ *
+ * Verifies that ECDH works for all curves
+ */
+TEST_P(KeyAgreementTest, Ecdh) {
+    // Because it's possible to use this API with keys on different curves, we
+    // check all N^2 combinations where N is the number of supported
+    // curves.
+    //
+    // This is not a big deal as N is 4 so we only do 16 runs. If we end up with a
+    // lot more curves we can be smart about things and just pick |otherCurve| so
+    // it's not |curve| and that way we end up with only 2*N runs
+    //
+    for (auto curve : ValidCurves()) {
+        for (auto localCurve : ValidCurves()) {
+            // Generate EC key locally (with access to private key material)
+            auto ecKey = EC_KEY_Ptr(EC_KEY_new());
+            int curveName = CurveToOpenSslCurveName(localCurve);
+            auto group = EC_GROUP_Ptr(EC_GROUP_new_by_curve_name(curveName));
+            ASSERT_NE(group, nullptr);
+            ASSERT_EQ(EC_KEY_set_group(ecKey.get(), group.get()), 1);
+            ASSERT_EQ(EC_KEY_generate_key(ecKey.get()), 1);
+            auto pkey = EVP_PKEY_Ptr(EVP_PKEY_new());
+            ASSERT_EQ(EVP_PKEY_set1_EC_KEY(pkey.get(), ecKey.get()), 1);
+
+            // Get encoded form of the public part of the locally generated key...
+            unsigned char* p = nullptr;
+            int encodedPublicKeySize = i2d_PUBKEY(pkey.get(), &p);
+            ASSERT_GT(encodedPublicKeySize, 0);
+            vector<uint8_t> encodedPublicKey(
+                    reinterpret_cast<const uint8_t*>(p),
+                    reinterpret_cast<const uint8_t*>(p + encodedPublicKeySize));
+            OPENSSL_free(p);
+
+            // Generate EC key in KeyMint (only access to public key material)
+            vector<uint8_t> challenge = {0x41, 0x42};
+            EXPECT_EQ(
+                    ErrorCode::OK,
+                    GenerateKey(AuthorizationSetBuilder()
+                                        .Authorization(TAG_NO_AUTH_REQUIRED)
+                                        .Authorization(TAG_EC_CURVE, curve)
+                                        .Authorization(TAG_PURPOSE, KeyPurpose::AGREE_KEY)
+                                        .Authorization(TAG_ALGORITHM, Algorithm::EC)
+                                        .Authorization(TAG_ATTESTATION_APPLICATION_ID, {0x61, 0x62})
+                                        .Authorization(TAG_ATTESTATION_CHALLENGE, challenge)
+                                        .SetDefaultValidity()))
+                    << "Failed to generate key";
+            ASSERT_GT(cert_chain_.size(), 0);
+            X509_Ptr kmKeyCert(parse_cert_blob(cert_chain_[0].encodedCertificate));
+            ASSERT_NE(kmKeyCert, nullptr);
+            // Check that keyAgreement (bit 4) is set in KeyUsage
+            EXPECT_TRUE((X509_get_key_usage(kmKeyCert.get()) & X509v3_KU_KEY_AGREEMENT) != 0);
+            auto kmPkey = EVP_PKEY_Ptr(X509_get_pubkey(kmKeyCert.get()));
+            ASSERT_NE(kmPkey, nullptr);
+            if (dump_Attestations) {
+                for (size_t n = 0; n < cert_chain_.size(); n++) {
+                    std::cout << bin2hex(cert_chain_[n].encodedCertificate) << std::endl;
+                }
+            }
+
+            // Now that we have the two keys, we ask KeyMint to perform ECDH...
+            if (curve != localCurve) {
+                // If the keys are using different curves KeyMint should fail with
+                // ErrorCode:INVALID_ARGUMENT. Check that.
+                EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+                string ZabFromKeyMintStr;
+                EXPECT_EQ(ErrorCode::INVALID_ARGUMENT,
+                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+                                 &ZabFromKeyMintStr));
+
+            } else {
+                // Otherwise if the keys are using the same curve, it should work.
+                EXPECT_EQ(ErrorCode::OK, Begin(KeyPurpose::AGREE_KEY, AuthorizationSetBuilder()));
+                string ZabFromKeyMintStr;
+                EXPECT_EQ(ErrorCode::OK,
+                          Finish(string(encodedPublicKey.begin(), encodedPublicKey.end()),
+                                 &ZabFromKeyMintStr));
+                vector<uint8_t> ZabFromKeyMint(ZabFromKeyMintStr.begin(), ZabFromKeyMintStr.end());
+
+                // Perform local ECDH between the two keys so we can check if we get the same Zab..
+                auto ctx = EVP_PKEY_CTX_Ptr(EVP_PKEY_CTX_new(pkey.get(), nullptr));
+                ASSERT_NE(ctx, nullptr);
+                ASSERT_EQ(EVP_PKEY_derive_init(ctx.get()), 1);
+                ASSERT_EQ(EVP_PKEY_derive_set_peer(ctx.get(), kmPkey.get()), 1);
+                size_t ZabFromTestLen = 0;
+                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), nullptr, &ZabFromTestLen), 1);
+                vector<uint8_t> ZabFromTest;
+                ZabFromTest.resize(ZabFromTestLen);
+                ASSERT_EQ(EVP_PKEY_derive(ctx.get(), ZabFromTest.data(), &ZabFromTestLen), 1);
+
+                EXPECT_EQ(ZabFromKeyMint, ZabFromTest);
+            }
+
+            CheckedDeleteKey();
+        }
+    }
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(KeyAgreementTest);
+
+using DestroyAttestationIdsTest = KeyMintAidlTestBase;
+
+// This is a problematic test, as it can render the device under test permanently unusable.
+// Re-enable and run at your own risk.
+TEST_P(DestroyAttestationIdsTest, DISABLED_DestroyTest) {
+    auto result = DestroyAttestationIds();
+    EXPECT_TRUE(result == ErrorCode::OK || result == ErrorCode::UNIMPLEMENTED);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(DestroyAttestationIdsTest);
+
+using EarlyBootKeyTest = KeyMintAidlTestBase;
+
+/*
+ * EarlyBootKeyTest.CreateEarlyBootKeys
+ *
+ * Verifies that creating early boot keys succeeds, even at a later stage (after boot).
+ */
+TEST_P(EarlyBootKeyTest, CreateEarlyBootKeys) {
+    // Early boot keys can be created after early boot.
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+
+    for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
+        ASSERT_GT(keyData.blob.size(), 0U);
+        AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
+    }
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+/*
+ * EarlyBootKeyTest.CreateAttestedEarlyBootKey
+ *
+ * Verifies that creating an early boot key with attestation succeeds.
+ */
+TEST_P(EarlyBootKeyTest, CreateAttestedEarlyBootKey) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] = CreateTestKeys(
+            TAG_EARLY_BOOT_ONLY, ErrorCode::OK, [](AuthorizationSetBuilder* builder) {
+                builder->AttestationChallenge("challenge");
+                builder->AttestationApplicationId("app_id");
+            });
+
+    for (const auto& keyData : {aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData}) {
+        ASSERT_GT(keyData.blob.size(), 0U);
+        AuthorizationSet crypto_params = SecLevelAuthorizations(keyData.characteristics);
+        EXPECT_TRUE(crypto_params.Contains(TAG_EARLY_BOOT_ONLY)) << crypto_params;
+    }
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+/*
+ * EarlyBootKeyTest.UseEarlyBootKeyFailure
+ *
+ * Verifies that using early boot keys at a later stage fails.
+ */
+TEST_P(EarlyBootKeyTest, UseEarlyBootKeyFailure) {
+    ASSERT_EQ(ErrorCode::OK, GenerateKey(AuthorizationSetBuilder()
+                                                 .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                 .Authorization(TAG_EARLY_BOOT_ONLY)
+                                                 .HmacKey(128)
+                                                 .Digest(Digest::SHA_2_256)
+                                                 .Authorization(TAG_MIN_MAC_LENGTH, 256)));
+    AuthorizationSet output_params;
+    EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, Begin(KeyPurpose::SIGN, key_blob_,
+                                                 AuthorizationSetBuilder()
+                                                         .Digest(Digest::SHA_2_256)
+                                                         .Authorization(TAG_MAC_LENGTH, 256),
+                                                 &output_params));
+}
+
+/*
+ * EarlyBootKeyTest.ImportEarlyBootKeyFailure
+ *
+ * Verifies that importing early boot keys fails.
+ */
+TEST_P(EarlyBootKeyTest, ImportEarlyBootKeyFailure) {
+    ASSERT_EQ(ErrorCode::EARLY_BOOT_ENDED, ImportKey(AuthorizationSetBuilder()
+                                                             .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                             .Authorization(TAG_EARLY_BOOT_ONLY)
+                                                             .EcdsaSigningKey(EcCurve::P_256)
+                                                             .Digest(Digest::SHA_2_256)
+                                                             .SetDefaultValidity(),
+                                                     KeyFormat::PKCS8, ec_256_key));
+}
+
+// This is a more comprehensive test, but it can only be run on a machine which is still in early
+// boot stage, which no proper Android device is by the time we can run VTS.  To use this,
+// un-disable it and modify vold to remove the call to earlyBootEnded().  Running the test will end
+// early boot, so you'll have to reboot between runs.
+TEST_P(EarlyBootKeyTest, DISABLED_FullTest) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::OK);
+    // TAG_EARLY_BOOT_ONLY should be in hw-enforced.
+    EXPECT_TRUE(HwEnforcedAuthorizations(aesKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(
+            HwEnforcedAuthorizations(hmacKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(HwEnforcedAuthorizations(rsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+    EXPECT_TRUE(
+            HwEnforcedAuthorizations(ecdsaKeyData.characteristics).Contains(TAG_EARLY_BOOT_ONLY));
+
+    // Should be able to use keys, since early boot has not ended
+    EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
+
+    // End early boot
+    ErrorCode earlyBootResult = GetReturnErrorCode(keyMint().earlyBootEnded());
+    EXPECT_EQ(earlyBootResult, ErrorCode::OK);
+
+    // Should not be able to use already-created keys.
+    EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseAesKey(aesKeyData.blob));
+    EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseHmacKey(hmacKeyData.blob));
+    EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseRsaKey(rsaKeyData.blob));
+    EXPECT_EQ(ErrorCode::EARLY_BOOT_ENDED, UseEcdsaKey(ecdsaKeyData.blob));
+
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+
+    // Should not be able to create new keys
+    std::tie(aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData) =
+            CreateTestKeys(TAG_EARLY_BOOT_ONLY, ErrorCode::EARLY_BOOT_ENDED);
+
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(EarlyBootKeyTest);
+
+using UnlockedDeviceRequiredTest = KeyMintAidlTestBase;
+
+// This may be a problematic test.  It can't be run repeatedly without unlocking the device in
+// between runs... and on most test devices there are no enrolled credentials so it can't be
+// unlocked at all, meaning the only way to get the test to pass again on a properly-functioning
+// device is to reboot it.  For that reason, this is disabled by default.  It can be used as part of
+// a manual test process, which includes unlocking between runs, which is why it's included here.
+// Well, that and the fact that it's the only test we can do without also making calls into the
+// Gatekeeper HAL.  We haven't written any cross-HAL tests, and don't know what all of the
+// implications might be, so that may or may not be a solution.
+TEST_P(UnlockedDeviceRequiredTest, DISABLED_KeysBecomeUnusable) {
+    auto [aesKeyData, hmacKeyData, rsaKeyData, ecdsaKeyData] =
+            CreateTestKeys(TAG_UNLOCKED_DEVICE_REQUIRED, ErrorCode::OK);
+
+    EXPECT_EQ(ErrorCode::OK, UseAesKey(aesKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseHmacKey(hmacKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseRsaKey(rsaKeyData.blob));
+    EXPECT_EQ(ErrorCode::OK, UseEcdsaKey(ecdsaKeyData.blob));
+
+    ErrorCode rc = GetReturnErrorCode(
+            keyMint().deviceLocked(false /* passwordOnly */, {} /* timestampToken */));
+    ASSERT_EQ(ErrorCode::OK, rc);
+    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseAesKey(aesKeyData.blob));
+    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseHmacKey(hmacKeyData.blob));
+    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseRsaKey(rsaKeyData.blob));
+    EXPECT_EQ(ErrorCode::DEVICE_LOCKED, UseEcdsaKey(ecdsaKeyData.blob));
+
+    CheckedDeleteKey(&aesKeyData.blob);
+    CheckedDeleteKey(&hmacKeyData.blob);
+    CheckedDeleteKey(&rsaKeyData.blob);
+    CheckedDeleteKey(&ecdsaKeyData.blob);
+}
+
+INSTANTIATE_KEYMINT_AIDL_TEST(UnlockedDeviceRequiredTest);
+
+}  // namespace aidl::android::hardware::security::keymint::test
+
+int main(int argc, char** argv) {
+    std::cout << "Testing ";
+    auto halInstances =
+            aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::build_params();
+    std::cout << "HAL instances:\n";
+    for (auto& entry : halInstances) {
+        std::cout << "    " << entry << '\n';
+    }
+
+    ::testing::InitGoogleTest(&argc, argv);
+    for (int i = 1; i < argc; ++i) {
+        if (argv[i][0] == '-') {
+            if (std::string(argv[i]) == "--arm_deleteAllKeys") {
+                aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
+                        arm_deleteAllKeys = true;
+            }
+            if (std::string(argv[i]) == "--dump_attestations") {
+                aidl::android::hardware::security::keymint::test::KeyMintAidlTestBase::
+                        dump_Attestations = true;
+            } else {
+                std::cout << "NOT dumping attestations" << std::endl;
+            }
+            // TODO(drysdale): Remove this flag when available KeyMint devices comply with spec
+            if (std::string(argv[i]) == "--check_patchLevels") {
+                aidl::android::hardware::security::keymint::test::check_patchLevels = true;
+            }
+        }
+    }
+    return RUN_ALL_TESTS();
+}
diff --git a/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
new file mode 100644
index 0000000..af951e8
--- /dev/null
+++ b/security/keymint/aidl/vts/functional/VtsRemotelyProvisionedComponentTests.cpp
@@ -0,0 +1,624 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "VtsRemotelyProvisionableComponentTests"
+
+#include <AndroidRemotelyProvisionedComponentDevice.h>
+#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <android/binder_manager.h>
+#include <cppbor_parse.h>
+#include <gmock/gmock.h>
+#include <keymaster/cppcose/cppcose.h>
+#include <keymaster/keymaster_configuration.h>
+#include <keymint_support/authorization_set.h>
+#include <openssl/ec.h>
+#include <openssl/ec_key.h>
+#include <openssl/x509.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <vector>
+
+#include "KeyMintAidlTestBase.h"
+
+namespace aidl::android::hardware::security::keymint::test {
+
+using ::std::string;
+using ::std::vector;
+
+namespace {
+
+#define INSTANTIATE_REM_PROV_AIDL_TEST(name)                                         \
+    INSTANTIATE_TEST_SUITE_P(                                                        \
+            PerInstance, name,                                                       \
+            testing::ValuesIn(VtsRemotelyProvisionedComponentTests::build_params()), \
+            ::android::PrintInstanceNameToString)
+
+using bytevec = std::vector<uint8_t>;
+using testing::MatchesRegex;
+using namespace remote_prov;
+using namespace keymaster;
+
+bytevec string_to_bytevec(const char* s) {
+    const uint8_t* p = reinterpret_cast<const uint8_t*>(s);
+    return bytevec(p, p + strlen(s));
+}
+
+ErrMsgOr<MacedPublicKey> corrupt_maced_key(const MacedPublicKey& macedPubKey) {
+    auto [coseMac0, _, mac0ParseErr] = cppbor::parse(macedPubKey.macedKey);
+    if (!coseMac0 || coseMac0->asArray()->size() != kCoseMac0EntryCount) {
+        return "COSE Mac0 parse failed";
+    }
+    auto protParams = coseMac0->asArray()->get(kCoseMac0ProtectedParams)->asBstr();
+    auto unprotParams = coseMac0->asArray()->get(kCoseMac0UnprotectedParams)->asMap();
+    auto payload = coseMac0->asArray()->get(kCoseMac0Payload)->asBstr();
+    auto tag = coseMac0->asArray()->get(kCoseMac0Tag)->asBstr();
+    if (!protParams || !unprotParams || !payload || !tag) {
+        return "Invalid COSE_Sign1: missing content";
+    }
+    auto corruptMac0 = cppbor::Array();
+    corruptMac0.add(protParams->clone());
+    corruptMac0.add(unprotParams->clone());
+    corruptMac0.add(payload->clone());
+    vector<uint8_t> tagData = tag->value();
+    tagData[0] ^= 0x08;
+    tagData[tagData.size() - 1] ^= 0x80;
+    corruptMac0.add(cppbor::Bstr(tagData));
+
+    return MacedPublicKey{corruptMac0.encode()};
+}
+
+ErrMsgOr<cppbor::Array> corrupt_sig(const cppbor::Array* coseSign1) {
+    if (coseSign1->size() != kCoseSign1EntryCount) {
+        return "Invalid COSE_Sign1, wrong entry count";
+    }
+    const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
+    const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
+    const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
+    const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
+    if (!protectedParams || !unprotectedParams || !payload || !signature) {
+        return "Invalid COSE_Sign1: missing content";
+    }
+
+    auto corruptSig = cppbor::Array();
+    corruptSig.add(protectedParams->clone());
+    corruptSig.add(unprotectedParams->clone());
+    corruptSig.add(payload->clone());
+    vector<uint8_t> sigData = signature->value();
+    sigData[0] ^= 0x08;
+    corruptSig.add(cppbor::Bstr(sigData));
+
+    return std::move(corruptSig);
+}
+
+ErrMsgOr<bytevec> corrupt_sig_chain(const bytevec& encodedEekChain, int which) {
+    auto [chain, _, parseErr] = cppbor::parse(encodedEekChain);
+    if (!chain || !chain->asArray()) {
+        return "EekChain parse failed";
+    }
+
+    cppbor::Array* eekChain = chain->asArray();
+    if (which >= eekChain->size()) {
+        return "selected sig out of range";
+    }
+    auto corruptChain = cppbor::Array();
+
+    for (int ii = 0; ii < eekChain->size(); ++ii) {
+        if (ii == which) {
+            auto sig = corrupt_sig(eekChain->get(which)->asArray());
+            if (!sig) {
+                return "Failed to build corrupted signature" + sig.moveMessage();
+            }
+            corruptChain.add(sig.moveValue());
+        } else {
+            corruptChain.add(eekChain->get(ii)->clone());
+        }
+    }
+    return corruptChain.encode();
+}
+
+string device_suffix(const string& name) {
+    size_t pos = name.find('/');
+    if (pos == string::npos) {
+        return name;
+    }
+    return name.substr(pos + 1);
+}
+
+bool matching_keymint_device(const string& rp_name, std::shared_ptr<IKeyMintDevice>* keyMint) {
+    string rp_suffix = device_suffix(rp_name);
+
+    vector<string> km_names = ::android::getAidlHalInstanceNames(IKeyMintDevice::descriptor);
+    for (const string& km_name : km_names) {
+        // If the suffix of the KeyMint instance equals the suffix of the
+        // RemotelyProvisionedComponent instance, assume they match.
+        if (device_suffix(km_name) == rp_suffix && AServiceManager_isDeclared(km_name.c_str())) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(km_name.c_str()));
+            *keyMint = IKeyMintDevice::fromBinder(binder);
+            return true;
+        }
+    }
+    return false;
+}
+
+}  // namespace
+
+class VtsRemotelyProvisionedComponentTests : public testing::TestWithParam<std::string> {
+  public:
+    virtual void SetUp() override {
+        if (AServiceManager_isDeclared(GetParam().c_str())) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+            provisionable_ = IRemotelyProvisionedComponent::fromBinder(binder);
+        }
+        ASSERT_NE(provisionable_, nullptr);
+    }
+
+    static vector<string> build_params() {
+        auto params = ::android::getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor);
+        return params;
+    }
+
+  protected:
+    std::shared_ptr<IRemotelyProvisionedComponent> provisionable_;
+};
+
+using GenerateKeyTests = VtsRemotelyProvisionedComponentTests;
+
+INSTANTIATE_REM_PROV_AIDL_TEST(GenerateKeyTests);
+
+/**
+ * Generate and validate a production-mode key.  MAC tag can't be verified, but
+ * the private key blob should be usable in KeyMint operations.
+ */
+TEST_P(GenerateKeyTests, generateEcdsaP256Key_prodMode) {
+    MacedPublicKey macedPubKey;
+    bytevec privateKeyBlob;
+    bool testMode = false;
+    auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+    ASSERT_TRUE(status.isOk());
+    vector<uint8_t> coseKeyData;
+    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+}
+
+/**
+ * Generate and validate a production-mode key, then use it as a KeyMint attestation key.
+ */
+TEST_P(GenerateKeyTests, generateAndUseEcdsaP256Key_prodMode) {
+    // See if there is a matching IKeyMintDevice for this IRemotelyProvisionedComponent.
+    std::shared_ptr<IKeyMintDevice> keyMint;
+    if (!matching_keymint_device(GetParam(), &keyMint)) {
+        // No matching IKeyMintDevice.
+        GTEST_SKIP() << "Skipping key use test as no matching KeyMint device found";
+        return;
+    }
+    KeyMintHardwareInfo info;
+    ASSERT_TRUE(keyMint->getHardwareInfo(&info).isOk());
+
+    MacedPublicKey macedPubKey;
+    bytevec privateKeyBlob;
+    bool testMode = false;
+    auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+    ASSERT_TRUE(status.isOk());
+    vector<uint8_t> coseKeyData;
+    check_maced_pubkey(macedPubKey, testMode, &coseKeyData);
+
+    AttestationKey attestKey;
+    attestKey.keyBlob = std::move(privateKeyBlob);
+    attestKey.issuerSubjectName = make_name_from_str("Android Keystore Key");
+
+    // Generate an ECDSA key that is attested by the generated P256 keypair.
+    AuthorizationSet keyDesc = AuthorizationSetBuilder()
+                                       .Authorization(TAG_NO_AUTH_REQUIRED)
+                                       .EcdsaSigningKey(256)
+                                       .AttestationChallenge("foo")
+                                       .AttestationApplicationId("bar")
+                                       .Digest(Digest::NONE)
+                                       .SetDefaultValidity();
+    KeyCreationResult creationResult;
+    auto result = keyMint->generateKey(keyDesc.vector_data(), attestKey, &creationResult);
+    ASSERT_TRUE(result.isOk());
+    vector<uint8_t> attested_key_blob = std::move(creationResult.keyBlob);
+    vector<KeyCharacteristics> attested_key_characteristics =
+            std::move(creationResult.keyCharacteristics);
+    vector<Certificate> attested_key_cert_chain = std::move(creationResult.certificateChain);
+    EXPECT_EQ(attested_key_cert_chain.size(), 1);
+
+    AuthorizationSet hw_enforced = HwEnforcedAuthorizations(attested_key_characteristics);
+    AuthorizationSet sw_enforced = SwEnforcedAuthorizations(attested_key_characteristics);
+    EXPECT_TRUE(verify_attestation_record("foo", "bar", sw_enforced, hw_enforced,
+                                          info.securityLevel,
+                                          attested_key_cert_chain[0].encodedCertificate));
+
+    // Attestation by itself is not valid (last entry is not self-signed).
+    EXPECT_FALSE(ChainSignaturesAreValid(attested_key_cert_chain));
+
+    // The signature over the attested key should correspond to the P256 public key.
+    X509_Ptr key_cert(parse_cert_blob(attested_key_cert_chain[0].encodedCertificate));
+    ASSERT_TRUE(key_cert.get());
+    EVP_PKEY_Ptr signing_pubkey;
+    p256_pub_key(coseKeyData, &signing_pubkey);
+    ASSERT_TRUE(signing_pubkey.get());
+
+    ASSERT_TRUE(X509_verify(key_cert.get(), signing_pubkey.get()))
+            << "Verification of attested certificate failed "
+            << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL);
+}
+
+/**
+ * Generate and validate a test-mode key.
+ */
+TEST_P(GenerateKeyTests, generateEcdsaP256Key_testMode) {
+    MacedPublicKey macedPubKey;
+    bytevec privateKeyBlob;
+    bool testMode = true;
+    auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &macedPubKey, &privateKeyBlob);
+    ASSERT_TRUE(status.isOk());
+
+    check_maced_pubkey(macedPubKey, testMode, nullptr);
+}
+
+class CertificateRequestTest : public VtsRemotelyProvisionedComponentTests {
+  protected:
+    CertificateRequestTest() : eekId_(string_to_bytevec("eekid")), challenge_(randomBytes(32)) {
+        generateTestEekChain(3);
+    }
+
+    void generateTestEekChain(size_t eekLength) {
+        auto chain = generateEekChain(eekLength, eekId_);
+        EXPECT_TRUE(chain) << chain.message();
+        if (chain) testEekChain_ = chain.moveValue();
+        testEekLength_ = eekLength;
+    }
+
+    void generateKeys(bool testMode, size_t numKeys) {
+        keysToSign_ = std::vector<MacedPublicKey>(numKeys);
+        cborKeysToSign_ = cppbor::Array();
+
+        for (auto& key : keysToSign_) {
+            bytevec privateKeyBlob;
+            auto status = provisionable_->generateEcdsaP256KeyPair(testMode, &key, &privateKeyBlob);
+            ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+            vector<uint8_t> payload_value;
+            check_maced_pubkey(key, testMode, &payload_value);
+            cborKeysToSign_.add(cppbor::EncodedItem(payload_value));
+        }
+    }
+
+    void checkProtectedData(const DeviceInfo& deviceInfo, const cppbor::Array& keysToSign,
+                            const bytevec& keysToSignMac, const ProtectedData& protectedData,
+                            std::vector<BccEntryData>* bccOutput = nullptr) {
+        auto [parsedProtectedData, _, protDataErrMsg] = cppbor::parse(protectedData.protectedData);
+        ASSERT_TRUE(parsedProtectedData) << protDataErrMsg;
+        ASSERT_TRUE(parsedProtectedData->asArray());
+        ASSERT_EQ(parsedProtectedData->asArray()->size(), kCoseEncryptEntryCount);
+
+        auto senderPubkey = getSenderPubKeyFromCoseEncrypt(parsedProtectedData);
+        ASSERT_TRUE(senderPubkey) << senderPubkey.message();
+        EXPECT_EQ(senderPubkey->second, eekId_);
+
+        auto sessionKey =
+                x25519_HKDF_DeriveKey(testEekChain_.last_pubkey, testEekChain_.last_privkey,
+                                      senderPubkey->first, false /* senderIsA */);
+        ASSERT_TRUE(sessionKey) << sessionKey.message();
+
+        auto protectedDataPayload =
+                decryptCoseEncrypt(*sessionKey, parsedProtectedData.get(), bytevec{} /* aad */);
+        ASSERT_TRUE(protectedDataPayload) << protectedDataPayload.message();
+
+        auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(*protectedDataPayload);
+        ASSERT_TRUE(parsedPayload) << "Failed to parse payload: " << payloadErrMsg;
+        ASSERT_TRUE(parsedPayload->asArray());
+        EXPECT_EQ(parsedPayload->asArray()->size(), 2U);
+
+        auto& signedMac = parsedPayload->asArray()->get(0);
+        auto& bcc = parsedPayload->asArray()->get(1);
+        ASSERT_TRUE(signedMac && signedMac->asArray());
+        ASSERT_TRUE(bcc && bcc->asArray());
+
+        // BCC is [ pubkey, + BccEntry]
+        auto bccContents = validateBcc(bcc->asArray());
+        ASSERT_TRUE(bccContents) << "\n" << bccContents.message() << "\n" << prettyPrint(bcc.get());
+        ASSERT_GT(bccContents->size(), 0U);
+
+        auto [deviceInfoMap, __2, deviceInfoErrMsg] = cppbor::parse(deviceInfo.deviceInfo);
+        ASSERT_TRUE(deviceInfoMap) << "Failed to parse deviceInfo: " << deviceInfoErrMsg;
+        ASSERT_TRUE(deviceInfoMap->asMap());
+
+        auto& signingKey = bccContents->back().pubKey;
+        auto macKey = verifyAndParseCoseSign1(signedMac->asArray(), signingKey,
+                                              cppbor::Array()  // SignedMacAad
+                                                      .add(challenge_)
+                                                      .add(std::move(deviceInfoMap))
+                                                      .add(keysToSignMac)
+                                                      .encode());
+        ASSERT_TRUE(macKey) << macKey.message();
+
+        auto coseMac0 = cppbor::Array()
+                                .add(cppbor::Map()  // protected
+                                             .add(ALGORITHM, HMAC_256)
+                                             .canonicalize()
+                                             .encode())
+                                .add(cppbor::Map())        // unprotected
+                                .add(keysToSign.encode())  // payload (keysToSign)
+                                .add(keysToSignMac);       // tag
+
+        auto macPayload = verifyAndParseCoseMac0(&coseMac0, *macKey);
+        ASSERT_TRUE(macPayload) << macPayload.message();
+
+        if (bccOutput) {
+            *bccOutput = std::move(*bccContents);
+        }
+    }
+
+    bytevec eekId_;
+    size_t testEekLength_;
+    EekChain testEekChain_;
+    bytevec challenge_;
+    std::vector<MacedPublicKey> keysToSign_;
+    cppbor::Array cborKeysToSign_;
+};
+
+/**
+ * Generate an empty certificate request in test mode, and decrypt and verify the structure and
+ * content.
+ */
+TEST_P(CertificateRequestTest, EmptyRequest_testMode) {
+    bool testMode = true;
+    for (size_t eekLength : {2, 3, 7}) {
+        SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+        generateTestEekChain(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+                &protectedData, &keysToSignMac);
+        ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+        checkProtectedData(deviceInfo, cppbor::Array(), keysToSignMac, protectedData);
+    }
+}
+
+/**
+ * Ensure that test mode outputs a unique BCC root key every time we request a
+ * certificate request. Else, it's possible that the test mode API could be used
+ * to fingerprint devices. Only the GEEK should be allowed to decrypt the same
+ * device public key multiple times.
+ */
+TEST_P(CertificateRequestTest, NewKeyPerCallInTestMode) {
+    constexpr bool testMode = true;
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    std::vector<BccEntryData> firstBcc;
+    checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
+                       &firstBcc);
+
+    status = provisionable_->generateCertificateRequest(
+            testMode, {} /* keysToSign */, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+    std::vector<BccEntryData> secondBcc;
+    checkProtectedData(deviceInfo, /*keysToSign=*/cppbor::Array(), keysToSignMac, protectedData,
+                       &secondBcc);
+
+    // Verify that none of the keys in the first BCC are repeated in the second one.
+    for (const auto& i : firstBcc) {
+        for (auto& j : secondBcc) {
+            ASSERT_THAT(i.pubKey, testing::Not(testing::ElementsAreArray(j.pubKey)))
+                    << "Found a repeated pubkey in two generateCertificateRequest test mode calls";
+        }
+    }
+}
+
+/**
+ * Generate an empty certificate request in prod mode. This test must be run explicitly, and
+ * is not run by default. Not all devices are GMS devices, and therefore they do not all
+ * trust the Google EEK root.
+ */
+TEST_P(CertificateRequestTest, DISABLED_EmptyRequest_prodMode) {
+    bool testMode = false;
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, {} /* keysToSign */, getProdEekChain(), challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    EXPECT_TRUE(status.isOk());
+}
+
+/**
+ * Generate a non-empty certificate request in test mode.  Decrypt, parse and validate the contents.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequest_testMode) {
+    bool testMode = true;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    for (size_t eekLength : {2, 3, 7}) {
+        SCOPED_TRACE(testing::Message() << "EEK of length " << eekLength);
+        generateTestEekChain(eekLength);
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(
+                testMode, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo, &protectedData,
+                &keysToSignMac);
+        ASSERT_TRUE(status.isOk()) << status.getMessage();
+
+        checkProtectedData(deviceInfo, cborKeysToSign_, keysToSignMac, protectedData);
+    }
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode. This test must be run explicitly, and
+ * is not run by default. Not all devices are GMS devices, and therefore they do not all
+ * trust the Google EEK root.
+ */
+TEST_P(CertificateRequestTest, DISABLED_NonEmptyRequest_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, keysToSign_, getProdEekChain(), challenge_, &deviceInfo, &protectedData,
+            &keysToSignMac);
+    EXPECT_TRUE(status.isOk());
+}
+
+/**
+ * Generate a non-empty certificate request in test mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_testMode) {
+    bool testMode = true;
+    generateKeys(testMode, 1 /* numKeys */);
+    MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, {keyWithCorruptMac}, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, but with the MAC corrupted on the keypair.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequestCorruptMac_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 1 /* numKeys */);
+    MacedPublicKey keyWithCorruptMac = corrupt_maced_key(keysToSign_[0]).moveValue();
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, {keyWithCorruptMac}, getProdEekChain(), challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk()) << status.getMessage();
+    EXPECT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_MAC);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has a corrupt EEK chain.
+ * Confirm that the request is rejected.
+ */
+TEST_P(CertificateRequestTest, NonEmptyCorruptEekRequest_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    auto prodEekChain = getProdEekChain();
+    auto [parsedChain, _, parseErr] = cppbor::parse(prodEekChain);
+    ASSERT_NE(parsedChain, nullptr) << parseErr;
+    ASSERT_NE(parsedChain->asArray(), nullptr);
+
+    for (int ii = 0; ii < parsedChain->asArray()->size(); ++ii) {
+        auto chain = corrupt_sig_chain(prodEekChain, ii);
+        ASSERT_TRUE(chain) << chain.message();
+
+        bytevec keysToSignMac;
+        DeviceInfo deviceInfo;
+        ProtectedData protectedData;
+        auto status = provisionable_->generateCertificateRequest(testMode, keysToSign_, *chain,
+                                                                 challenge_, &deviceInfo,
+                                                                 &protectedData, &keysToSignMac);
+        ASSERT_FALSE(status.isOk());
+        ASSERT_EQ(status.getServiceSpecificError(),
+                  BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+    }
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode that has an incomplete EEK chain.
+ * Confirm that the request is rejected.
+ */
+TEST_P(CertificateRequestTest, NonEmptyIncompleteEekRequest_prodMode) {
+    bool testMode = false;
+    generateKeys(testMode, 4 /* numKeys */);
+
+    // Build an EEK chain that omits the first self-signed cert.
+    auto truncatedChain = cppbor::Array();
+    auto [chain, _, parseErr] = cppbor::parse(getProdEekChain());
+    ASSERT_TRUE(chain);
+    auto eekChain = chain->asArray();
+    ASSERT_NE(eekChain, nullptr);
+    for (size_t ii = 1; ii < eekChain->size(); ii++) {
+        truncatedChain.add(eekChain->get(ii)->clone());
+    }
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            testMode, keysToSign_, truncatedChain.encode(), challenge_, &deviceInfo, &protectedData,
+            &keysToSignMac);
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(status.getServiceSpecificError(), BnRemotelyProvisionedComponent::STATUS_INVALID_EEK);
+}
+
+/**
+ * Generate a non-empty certificate request in test mode, with prod keys.  Must fail with
+ * STATUS_PRODUCTION_KEY_IN_TEST_REQUEST.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequest_prodKeyInTestCert) {
+    generateKeys(false /* testMode */, 2 /* numKeys */);
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            true /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(status.getServiceSpecificError(),
+              BnRemotelyProvisionedComponent::STATUS_PRODUCTION_KEY_IN_TEST_REQUEST);
+}
+
+/**
+ * Generate a non-empty certificate request in prod mode, with test keys.  Must fail with
+ * STATUS_TEST_KEY_IN_PRODUCTION_REQUEST.
+ */
+TEST_P(CertificateRequestTest, NonEmptyRequest_testKeyInProdCert) {
+    generateKeys(true /* testMode */, 2 /* numKeys */);
+
+    bytevec keysToSignMac;
+    DeviceInfo deviceInfo;
+    ProtectedData protectedData;
+    auto status = provisionable_->generateCertificateRequest(
+            false /* testMode */, keysToSign_, testEekChain_.chain, challenge_, &deviceInfo,
+            &protectedData, &keysToSignMac);
+    ASSERT_FALSE(status.isOk());
+    ASSERT_EQ(status.getServiceSpecificError(),
+              BnRemotelyProvisionedComponent::STATUS_TEST_KEY_IN_PRODUCTION_REQUEST);
+}
+
+INSTANTIATE_REM_PROV_AIDL_TEST(CertificateRequestTest);
+
+}  // namespace aidl::android::hardware::security::keymint::test
diff --git a/security/keymint/aidl/vts/performance/Android.bp b/security/keymint/aidl/vts/performance/Android.bp
new file mode 100644
index 0000000..79ed0d5
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/Android.bp
@@ -0,0 +1,47 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_benchmark {
+    name: "VtsAidlKeyMintBenchmarkTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "KeyMintBenchmark.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+        "libkeymint",
+        "libkeymint_support",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libcppbor_external",
+        "libchrome",
+    ],
+}
diff --git a/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
new file mode 100644
index 0000000..54b6fdc
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/KeyMintBenchmark.cpp
@@ -0,0 +1,657 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "keymint_benchmark"
+
+#include <base/command_line.h>
+#include <benchmark/benchmark.h>
+#include <iostream>
+
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+#include <android/binder_manager.h>
+#include <binder/IServiceManager.h>
+#include <keymint_support/authorization_set.h>
+
+#define SMALL_MESSAGE_SIZE 64
+#define MEDIUM_MESSAGE_SIZE 1024
+#define LARGE_MESSAGE_SIZE 131072
+
+namespace aidl::android::hardware::security::keymint::test {
+
+::std::ostream& operator<<(::std::ostream& os, const keymint::AuthorizationSet& set);
+
+using ::android::sp;
+using Status = ::ndk::ScopedAStatus;
+using ::std::optional;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class KeyMintBenchmarkTest {
+  public:
+    KeyMintBenchmarkTest() {
+        message_cache_.push_back(string(SMALL_MESSAGE_SIZE, 'x'));
+        message_cache_.push_back(string(MEDIUM_MESSAGE_SIZE, 'x'));
+        message_cache_.push_back(string(LARGE_MESSAGE_SIZE, 'x'));
+    }
+
+    static KeyMintBenchmarkTest* newInstance(const char* instanceName) {
+        if (AServiceManager_isDeclared(instanceName)) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(instanceName));
+            KeyMintBenchmarkTest* test = new KeyMintBenchmarkTest();
+            test->InitializeKeyMint(IKeyMintDevice::fromBinder(binder));
+            return test;
+        } else {
+            return nullptr;
+        }
+    }
+
+    int getError() { return static_cast<int>(error_); }
+
+    const string& GenerateMessage(int size) {
+        for (const string& message : message_cache_) {
+            if (message.size() == size) {
+                return message;
+            }
+        }
+        string message = string(size, 'x');
+        message_cache_.push_back(message);
+        return std::move(message);
+    }
+
+    optional<BlockMode> getBlockMode(string transform) {
+        if (transform.find("/ECB") != string::npos) {
+            return BlockMode::ECB;
+        } else if (transform.find("/CBC") != string::npos) {
+            return BlockMode::CBC;
+        } else if (transform.find("/CTR") != string::npos) {
+            return BlockMode::CTR;
+        } else if (transform.find("/GCM") != string::npos) {
+            return BlockMode::GCM;
+        }
+        return {};
+    }
+
+    PaddingMode getPadding(string transform, bool sign) {
+        if (transform.find("/PKCS7") != string::npos) {
+            return PaddingMode::PKCS7;
+        } else if (transform.find("/PSS") != string::npos) {
+            return PaddingMode::RSA_PSS;
+        } else if (transform.find("/OAEP") != string::npos) {
+            return PaddingMode::RSA_OAEP;
+        } else if (transform.find("/PKCS1") != string::npos) {
+            return sign ? PaddingMode::RSA_PKCS1_1_5_SIGN : PaddingMode::RSA_PKCS1_1_5_ENCRYPT;
+        } else if (sign && transform.find("RSA") != string::npos) {
+            // RSA defaults to PKCS1 for sign
+            return PaddingMode::RSA_PKCS1_1_5_SIGN;
+        }
+        return PaddingMode::NONE;
+    }
+
+    optional<Algorithm> getAlgorithm(string transform) {
+        if (transform.find("AES") != string::npos) {
+            return Algorithm::AES;
+        } else if (transform.find("Hmac") != string::npos) {
+            return Algorithm::HMAC;
+        } else if (transform.find("DESede") != string::npos) {
+            return Algorithm::TRIPLE_DES;
+        } else if (transform.find("RSA") != string::npos) {
+            return Algorithm::RSA;
+        } else if (transform.find("EC") != string::npos) {
+            return Algorithm::EC;
+        }
+        std::cerr << "Can't find algorithm for " << transform << std::endl;
+        return {};
+    }
+
+    Digest getDigest(string transform) {
+        if (transform.find("MD5") != string::npos) {
+            return Digest::MD5;
+        } else if (transform.find("SHA1") != string::npos ||
+                   transform.find("SHA-1") != string::npos) {
+            return Digest::SHA1;
+        } else if (transform.find("SHA224") != string::npos) {
+            return Digest::SHA_2_224;
+        } else if (transform.find("SHA256") != string::npos) {
+            return Digest::SHA_2_256;
+        } else if (transform.find("SHA384") != string::npos) {
+            return Digest::SHA_2_384;
+        } else if (transform.find("SHA512") != string::npos) {
+            return Digest::SHA_2_512;
+        } else if (transform.find("RSA") != string::npos &&
+                   transform.find("OAEP") != string::npos) {
+            return Digest::SHA1;
+        } else if (transform.find("Hmac") != string::npos) {
+            return Digest::SHA_2_256;
+        }
+        return Digest::NONE;
+    }
+
+    bool GenerateKey(string transform, int keySize, bool sign = false) {
+        if (transform == key_transform_) {
+            return true;
+        } else if (key_transform_ != "") {
+            // Deleting old key first
+            key_transform_ = "";
+            if (DeleteKey() != ErrorCode::OK) {
+                return false;
+            }
+        }
+        std::optional<Algorithm> algorithm = getAlgorithm(transform);
+        if (!algorithm) {
+            std::cerr << "Error: invalid algorithm " << transform << std::endl;
+            return false;
+        }
+        key_transform_ = transform;
+        AuthorizationSetBuilder authSet = AuthorizationSetBuilder()
+                                                  .Authorization(TAG_NO_AUTH_REQUIRED)
+                                                  .Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT)
+                                                  .Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT)
+                                                  .Authorization(TAG_PURPOSE, KeyPurpose::SIGN)
+                                                  .Authorization(TAG_PURPOSE, KeyPurpose::VERIFY)
+                                                  .Authorization(TAG_KEY_SIZE, keySize)
+                                                  .Authorization(TAG_ALGORITHM, algorithm.value())
+                                                  .Digest(getDigest(transform))
+                                                  .Padding(getPadding(transform, sign));
+        std::optional<BlockMode> blockMode = getBlockMode(transform);
+        if (blockMode) {
+            authSet.BlockMode(blockMode.value());
+            if (blockMode == BlockMode::GCM) {
+                authSet.Authorization(TAG_MIN_MAC_LENGTH, 128);
+            }
+        }
+        if (algorithm == Algorithm::HMAC) {
+            authSet.Authorization(TAG_MIN_MAC_LENGTH, 128);
+        }
+        if (algorithm == Algorithm::RSA) {
+            authSet.Authorization(TAG_RSA_PUBLIC_EXPONENT, 65537U);
+            authSet.SetDefaultValidity();
+        }
+        if (algorithm == Algorithm::EC) {
+            authSet.SetDefaultValidity();
+        }
+        error_ = GenerateKey(authSet);
+        return error_ == ErrorCode::OK;
+    }
+
+    AuthorizationSet getOperationParams(string transform, bool sign = false) {
+        AuthorizationSetBuilder builder = AuthorizationSetBuilder()
+                                                  .Padding(getPadding(transform, sign))
+                                                  .Digest(getDigest(transform));
+        std::optional<BlockMode> blockMode = getBlockMode(transform);
+        if (sign && (transform.find("Hmac") != string::npos)) {
+            builder.Authorization(TAG_MAC_LENGTH, 128);
+        }
+        if (blockMode) {
+            builder.BlockMode(*blockMode);
+            if (blockMode == BlockMode::GCM) {
+                builder.Authorization(TAG_MAC_LENGTH, 128);
+            }
+        }
+        return std::move(builder);
+    }
+
+    optional<string> Process(const string& message, const string& signature = "") {
+        ErrorCode result;
+
+        string output;
+        result = Finish(message, signature, &output);
+        if (result != ErrorCode::OK) {
+            error_ = result;
+            return {};
+        }
+        return output;
+    }
+
+    ErrorCode DeleteKey() {
+        Status result = keymint_->deleteKey(key_blob_);
+        key_blob_ = vector<uint8_t>();
+        return GetReturnErrorCode(result);
+    }
+
+    ErrorCode Begin(KeyPurpose purpose, const AuthorizationSet& in_params,
+                    AuthorizationSet* out_params) {
+        Status result;
+        BeginResult out;
+        result = keymint_->begin(purpose, key_blob_, in_params.vector_data(), std::nullopt, &out);
+        if (result.isOk()) {
+            *out_params = out.params;
+            op_ = out.operation;
+        }
+        return GetReturnErrorCode(result);
+    }
+
+    SecurityLevel securityLevel_;
+    string name_;
+
+  private:
+    ErrorCode GenerateKey(const AuthorizationSet& key_desc,
+                          const optional<AttestationKey>& attest_key = std::nullopt) {
+        key_blob_.clear();
+        KeyCreationResult creationResult;
+        Status result = keymint_->generateKey(key_desc.vector_data(), attest_key, &creationResult);
+        if (result.isOk()) {
+            key_blob_ = std::move(creationResult.keyBlob);
+            creationResult.keyCharacteristics.clear();
+            creationResult.certificateChain.clear();
+        }
+        return GetReturnErrorCode(result);
+    }
+
+    void InitializeKeyMint(std::shared_ptr<IKeyMintDevice> keyMint) {
+        if (!keyMint) {
+            std::cerr << "Trying initialize nullptr in InitializeKeyMint" << std::endl;
+            return;
+        }
+        keymint_ = std::move(keyMint);
+        KeyMintHardwareInfo info;
+        Status result = keymint_->getHardwareInfo(&info);
+        if (!result.isOk()) {
+            std::cerr << "InitializeKeyMint: getHardwareInfo failed with "
+                      << result.getServiceSpecificError() << std::endl;
+        }
+        securityLevel_ = info.securityLevel;
+        name_.assign(info.keyMintName.begin(), info.keyMintName.end());
+    }
+
+    ErrorCode Finish(const string& input, const string& signature, string* output) {
+        if (!op_) {
+            std::cerr << "Finish: Operation is nullptr" << std::endl;
+            return ErrorCode::UNEXPECTED_NULL_POINTER;
+        }
+
+        vector<uint8_t> oPut;
+        Status result =
+                op_->finish(vector<uint8_t>(input.begin(), input.end()),
+                            vector<uint8_t>(signature.begin(), signature.end()), {} /* authToken */,
+                            {} /* timestampToken */, {} /* confirmationToken */, &oPut);
+
+        if (result.isOk()) output->append(oPut.begin(), oPut.end());
+
+        op_.reset();
+        return GetReturnErrorCode(result);
+    }
+
+    ErrorCode Update(const string& input, string* output) {
+        Status result;
+        if (!op_) {
+            std::cerr << "Update: Operation is nullptr" << std::endl;
+            return ErrorCode::UNEXPECTED_NULL_POINTER;
+        }
+
+        std::vector<uint8_t> o_put;
+        result = op_->update(vector<uint8_t>(input.begin(), input.end()), {} /* authToken */,
+                             {} /* timestampToken */, &o_put);
+
+        if (result.isOk() && output) *output = {o_put.begin(), o_put.end()};
+        return GetReturnErrorCode(result);
+    }
+
+    ErrorCode GetReturnErrorCode(const Status& result) {
+        error_ = static_cast<ErrorCode>(result.getServiceSpecificError());
+        if (result.isOk()) return ErrorCode::OK;
+
+        if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+            return static_cast<ErrorCode>(result.getServiceSpecificError());
+        }
+
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    std::shared_ptr<IKeyMintOperation> op_;
+    vector<Certificate> cert_chain_;
+    vector<uint8_t> key_blob_;
+    vector<KeyCharacteristics> key_characteristics_;
+    std::shared_ptr<IKeyMintDevice> keymint_;
+    std::vector<string> message_cache_;
+    std::string key_transform_;
+    ErrorCode error_;
+};
+
+KeyMintBenchmarkTest* keymintTest;
+
+static void settings(benchmark::internal::Benchmark* benchmark) {
+    benchmark->Unit(benchmark::kMillisecond);
+}
+
+static void addDefaultLabel(benchmark::State& state) {
+    std::string secLevel;
+    switch (keymintTest->securityLevel_) {
+        case SecurityLevel::STRONGBOX:
+            secLevel = "STRONGBOX";
+            break;
+        case SecurityLevel::SOFTWARE:
+            secLevel = "SOFTWARE";
+            break;
+        case SecurityLevel::TRUSTED_ENVIRONMENT:
+            secLevel = "TEE";
+            break;
+        case SecurityLevel::KEYSTORE:
+            secLevel = "KEYSTORE";
+            break;
+    }
+    state.SetLabel("hardware_name:" + keymintTest->name_ + " sec_level:" + secLevel);
+}
+
+// clang-format off
+#define BENCHMARK_KM(func, transform, keySize) \
+    BENCHMARK_CAPTURE(func, transform/keySize, #transform "/" #keySize, keySize)->Apply(settings);
+#define BENCHMARK_KM_MSG(func, transform, keySize, msgSize)                                      \
+    BENCHMARK_CAPTURE(func, transform/keySize/msgSize, #transform "/" #keySize "/" #msgSize, \
+                      keySize, msgSize)                                                          \
+            ->Apply(settings);
+
+#define BENCHMARK_KM_ALL_MSGS(func, transform, keySize)             \
+    BENCHMARK_KM_MSG(func, transform, keySize, SMALL_MESSAGE_SIZE)  \
+    BENCHMARK_KM_MSG(func, transform, keySize, MEDIUM_MESSAGE_SIZE) \
+    BENCHMARK_KM_MSG(func, transform, keySize, LARGE_MESSAGE_SIZE)
+
+#define BENCHMARK_KM_CIPHER(transform, keySize, msgSize)   \
+    BENCHMARK_KM_MSG(encrypt, transform, keySize, msgSize) \
+    BENCHMARK_KM_MSG(decrypt, transform, keySize, msgSize)
+
+#define BENCHMARK_KM_CIPHER_ALL_MSGS(transform, keySize) \
+    BENCHMARK_KM_ALL_MSGS(encrypt, transform, keySize)   \
+    BENCHMARK_KM_ALL_MSGS(decrypt, transform, keySize)
+
+#define BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, keySize) \
+    BENCHMARK_KM_ALL_MSGS(sign, transform, keySize)         \
+    BENCHMARK_KM_ALL_MSGS(verify, transform, keySize)
+// clang-format on
+
+/*
+ * ============= KeyGen TESTS ==================
+ */
+static void keygen(benchmark::State& state, string transform, int keySize) {
+    addDefaultLabel(state);
+    for (auto _ : state) {
+        if (!keymintTest->GenerateKey(transform, keySize)) {
+            state.SkipWithError(
+                    ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+        }
+        state.PauseTiming();
+
+        keymintTest->DeleteKey();
+        state.ResumeTiming();
+    }
+}
+
+BENCHMARK_KM(keygen, AES, 128);
+BENCHMARK_KM(keygen, AES, 256);
+
+BENCHMARK_KM(keygen, RSA, 2048);
+BENCHMARK_KM(keygen, RSA, 3072);
+BENCHMARK_KM(keygen, RSA, 4096);
+
+BENCHMARK_KM(keygen, EC, 224);
+BENCHMARK_KM(keygen, EC, 256);
+BENCHMARK_KM(keygen, EC, 384);
+BENCHMARK_KM(keygen, EC, 521);
+
+BENCHMARK_KM(keygen, DESede, 168);
+
+BENCHMARK_KM(keygen, Hmac, 64);
+BENCHMARK_KM(keygen, Hmac, 128);
+BENCHMARK_KM(keygen, Hmac, 256);
+BENCHMARK_KM(keygen, Hmac, 512);
+
+/*
+ * ============= SIGNATURE TESTS ==================
+ */
+
+static void sign(benchmark::State& state, string transform, int keySize, int msgSize) {
+    addDefaultLabel(state);
+    if (!keymintTest->GenerateKey(transform, keySize, true)) {
+        state.SkipWithError(
+                ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+
+    auto in_params = keymintTest->getOperationParams(transform, true);
+    AuthorizationSet out_params;
+    string message = keymintTest->GenerateMessage(msgSize);
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        ErrorCode error = keymintTest->Begin(KeyPurpose::SIGN, in_params, &out_params);
+        if (error != ErrorCode::OK) {
+            state.SkipWithError(
+                    ("Error beginning sign, " + std::to_string(keymintTest->getError())).c_str());
+            return;
+        }
+        state.ResumeTiming();
+        out_params.Clear();
+        if (!keymintTest->Process(message)) {
+            state.SkipWithError(("Sign error, " + std::to_string(keymintTest->getError())).c_str());
+            break;
+        }
+    }
+}
+
+static void verify(benchmark::State& state, string transform, int keySize, int msgSize) {
+    addDefaultLabel(state);
+    if (!keymintTest->GenerateKey(transform, keySize, true)) {
+        state.SkipWithError(
+                ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    AuthorizationSet out_params;
+    auto in_params = keymintTest->getOperationParams(transform, true);
+    string message = keymintTest->GenerateMessage(msgSize);
+    ErrorCode error = keymintTest->Begin(KeyPurpose::SIGN, in_params, &out_params);
+    if (error != ErrorCode::OK) {
+        state.SkipWithError(
+                ("Error beginning sign, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    std::optional<string> signature = keymintTest->Process(message);
+    if (!signature) {
+        state.SkipWithError(("Sign error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    out_params.Clear();
+    if (transform.find("Hmac") != string::npos) {
+        in_params = keymintTest->getOperationParams(transform, false);
+    }
+    for (auto _ : state) {
+        state.PauseTiming();
+        error = keymintTest->Begin(KeyPurpose::VERIFY, in_params, &out_params);
+        if (error != ErrorCode::OK) {
+            state.SkipWithError(
+                    ("Verify begin error, " + std::to_string(keymintTest->getError())).c_str());
+            return;
+        }
+        state.ResumeTiming();
+        if (!keymintTest->Process(message, *signature)) {
+            state.SkipWithError(
+                    ("Verify error, " + std::to_string(keymintTest->getError())).c_str());
+            break;
+        }
+    }
+}
+
+// clang-format off
+#define BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(transform) \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 64)      \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 128)     \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 256)     \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 512)
+
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA1)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA256)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA224)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA256)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA384)
+BENCHMARK_KM_SIGNATURE_ALL_HMAC_KEYS(HmacSHA512)
+
+#define BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(transform) \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 224)      \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 256)      \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 384)      \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 521)
+
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(NONEwithECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA1withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA224withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA256withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA384withECDSA);
+BENCHMARK_KM_SIGNATURE_ALL_ECDSA_KEYS(SHA512withECDSA);
+
+#define BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(transform) \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 2048)   \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 3072)   \
+    BENCHMARK_KM_SIGNATURE_ALL_MSGS(transform, 4096)
+
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(MD5withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA1withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA);
+
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(MD5withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA1withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA224withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA384withRSA/PSS);
+BENCHMARK_KM_SIGNATURE_ALL_RSA_KEYS(SHA512withRSA/PSS);
+// clang-format on
+
+/*
+ * ============= CIPHER TESTS ==================
+ */
+
+static void encrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+    addDefaultLabel(state);
+    if (!keymintTest->GenerateKey(transform, keySize)) {
+        state.SkipWithError(
+                ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    auto in_params = keymintTest->getOperationParams(transform);
+    AuthorizationSet out_params;
+    string message = keymintTest->GenerateMessage(msgSize);
+
+    for (auto _ : state) {
+        state.PauseTiming();
+        auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
+        if (error != ErrorCode::OK) {
+            state.SkipWithError(
+                    ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+            return;
+        }
+        out_params.Clear();
+        state.ResumeTiming();
+        if (!keymintTest->Process(message)) {
+            state.SkipWithError(
+                    ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
+            break;
+        }
+    }
+}
+
+static void decrypt(benchmark::State& state, string transform, int keySize, int msgSize) {
+    addDefaultLabel(state);
+    if (!keymintTest->GenerateKey(transform, keySize)) {
+        state.SkipWithError(
+                ("Key generation error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    AuthorizationSet out_params;
+    AuthorizationSet in_params = keymintTest->getOperationParams(transform);
+    string message = keymintTest->GenerateMessage(msgSize);
+    auto error = keymintTest->Begin(KeyPurpose::ENCRYPT, in_params, &out_params);
+    if (error != ErrorCode::OK) {
+        state.SkipWithError(
+                ("Encryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    auto encryptedMessage = keymintTest->Process(message);
+    if (!encryptedMessage) {
+        state.SkipWithError(
+                ("Encryption error, " + std::to_string(keymintTest->getError())).c_str());
+        return;
+    }
+    in_params.push_back(out_params);
+    out_params.Clear();
+    for (auto _ : state) {
+        state.PauseTiming();
+        error = keymintTest->Begin(KeyPurpose::DECRYPT, in_params, &out_params);
+        if (error != ErrorCode::OK) {
+            state.SkipWithError(
+                    ("Decryption begin error, " + std::to_string(keymintTest->getError())).c_str());
+            return;
+        }
+        state.ResumeTiming();
+        if (!keymintTest->Process(*encryptedMessage)) {
+            state.SkipWithError(
+                    ("Decryption error, " + std::to_string(keymintTest->getError())).c_str());
+            break;
+        }
+    }
+}
+
+// clang-format off
+// AES
+#define BENCHMARK_KM_CIPHER_ALL_AES_KEYS(transform) \
+    BENCHMARK_KM_CIPHER_ALL_MSGS(transform, 128)    \
+    BENCHMARK_KM_CIPHER_ALL_MSGS(transform, 256)
+
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CBC/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CBC/PKCS7Padding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/CTR/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/ECB/NoPadding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/ECB/PKCS7Padding);
+BENCHMARK_KM_CIPHER_ALL_AES_KEYS(AES/GCM/NoPadding);
+
+// Triple DES
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/CBC/NoPadding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/CBC/PKCS7Padding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/ECB/NoPadding, 168);
+BENCHMARK_KM_CIPHER_ALL_MSGS(DESede/ECB/PKCS7Padding, 168);
+
+#define BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(transform, msgSize) \
+    BENCHMARK_KM_CIPHER(transform, 2048, msgSize)            \
+    BENCHMARK_KM_CIPHER(transform, 3072, msgSize)            \
+    BENCHMARK_KM_CIPHER(transform, 4096, msgSize)
+
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/NoPadding, SMALL_MESSAGE_SIZE);
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/PKCS1Padding, SMALL_MESSAGE_SIZE);
+BENCHMARK_KM_CIPHER_ALL_RSA_KEYS(RSA/ECB/OAEPPadding, SMALL_MESSAGE_SIZE);
+
+// clang-format on
+}  // namespace aidl::android::hardware::security::keymint::test
+
+int main(int argc, char** argv) {
+    ::benchmark::Initialize(&argc, argv);
+    base::CommandLine::Init(argc, argv);
+    base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+    auto service_name = command_line->GetSwitchValueASCII("service_name");
+    if (service_name.empty()) {
+        service_name =
+                std::string(
+                        aidl::android::hardware::security::keymint::IKeyMintDevice::descriptor) +
+                "/default";
+    }
+    std::cerr << service_name << std::endl;
+    aidl::android::hardware::security::keymint::test::keymintTest =
+            aidl::android::hardware::security::keymint::test::KeyMintBenchmarkTest::newInstance(
+                    service_name.c_str());
+    if (!aidl::android::hardware::security::keymint::test::keymintTest) {
+        return 1;
+    }
+    ::benchmark::RunSpecifiedBenchmarks();
+}
diff --git a/security/keymint/aidl/vts/performance/README b/security/keymint/aidl/vts/performance/README
new file mode 100644
index 0000000..1221ad8
--- /dev/null
+++ b/security/keymint/aidl/vts/performance/README
@@ -0,0 +1,28 @@
+# KeyMint Benchmark
+
+The KeyMint Benchmark is a standalone tool for measuring the performance of
+ KeyMint implementations.
+
+## Building
+
+Build:
+`m  VtsAidlKeyMintBenchmarkTest`
+
+Transfer to device/emulator:
+`adb sync data`
+
+The benchmark executable will be located at
+ `data/benchmarktest/VtsAidlKeyMintBenchmarkTest` on the device.
+
+## Usage
+
+KeyMint Benchmark is built on [Google microbenchmark
+library](https://github.com/google/benchmark). All of the commandline arguments
+provided by the microbenchmark library are valid, such as
+`--benchmark_filter=<regex>` or `benchmark_out_format={json|console|csv}`.
+In addition to the command line arguments provided by microbenchmark,
+`--service_name=<service_name>` is provided to allow specification of the KeyMint
+fully qualified service name, e.g. specify
+`--service_name=android.hardware.security.keymint.IKeyMintDevice/default` to
+benchmark default implementation of KeyMint.
+
diff --git a/security/keymint/support/Android.bp b/security/keymint/support/Android.bp
new file mode 100644
index 0000000..9e218b6
--- /dev/null
+++ b/security/keymint/support/Android.bp
@@ -0,0 +1,84 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_library {
+    name: "libkeymint_support",
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+    ],
+    srcs: [
+        "attestation_record.cpp",
+        "authorization_set.cpp",
+        "keymint_utils.cpp",
+        "key_param_output.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "libbase",
+        "libcrypto",
+        "libutils",
+    ],
+}
+
+cc_library {
+    name: "libkeymint_remote_prov_support",
+    vendor_available: true,
+    srcs: [
+        "remote_prov_utils.cpp",
+    ],
+    export_include_dirs: [
+        "include",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libcrypto",
+        "libjsoncpp",
+    ],
+}
+
+cc_test {
+    name: "libkeymint_remote_prov_support_test",
+    srcs: ["remote_prov_utils_test.cpp"],
+    static_libs: [
+        "libgmock",
+        "libgtest_main",
+    ],
+    shared_libs: [
+        "libbase",
+        "libcppbor_external",
+        "libcppcose_rkp",
+        "libcrypto",
+        "libjsoncpp",
+        "libkeymaster_portable",
+        "libkeymint_remote_prov_support",
+    ],
+}
diff --git a/security/keymint/support/OWNERS b/security/keymint/support/OWNERS
new file mode 100644
index 0000000..a93b171
--- /dev/null
+++ b/security/keymint/support/OWNERS
@@ -0,0 +1,4 @@
+jbires@google.com
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/keymint/support/attestation_record.cpp b/security/keymint/support/attestation_record.cpp
new file mode 100644
index 0000000..2462228
--- /dev/null
+++ b/security/keymint/support/attestation_record.cpp
@@ -0,0 +1,421 @@
+/*
+ * Copyright 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 <keymint_support/attestation_record.h>
+
+#include <assert.h>
+
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
+
+#include <android-base/logging.h>
+
+#include <openssl/asn1t.h>
+#include <openssl/bn.h>
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+
+#define AT __FILE__ ":" << __LINE__
+
+namespace aidl::android::hardware::security::keymint {
+
+struct stack_st_ASN1_TYPE_Delete {
+    void operator()(stack_st_ASN1_TYPE* p) { sk_ASN1_TYPE_free(p); }
+};
+
+struct ASN1_STRING_Delete {
+    void operator()(ASN1_STRING* p) { ASN1_STRING_free(p); }
+};
+
+struct ASN1_TYPE_Delete {
+    void operator()(ASN1_TYPE* p) { ASN1_TYPE_free(p); }
+};
+
+#define ASN1_INTEGER_SET STACK_OF(ASN1_INTEGER)
+
+typedef struct km_root_of_trust {
+    ASN1_OCTET_STRING* verified_boot_key;
+    ASN1_BOOLEAN device_locked;
+    ASN1_ENUMERATED* verified_boot_state;
+    ASN1_OCTET_STRING* verified_boot_hash;
+} KM_ROOT_OF_TRUST;
+
+ASN1_SEQUENCE(KM_ROOT_OF_TRUST) = {
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_key, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, device_locked, ASN1_BOOLEAN),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_state, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_ROOT_OF_TRUST, verified_boot_hash, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(KM_ROOT_OF_TRUST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_ROOT_OF_TRUST);
+
+// Fields ordered in tag order.
+typedef struct km_auth_list {
+    ASN1_INTEGER_SET* purpose;
+    ASN1_INTEGER* algorithm;
+    ASN1_INTEGER* key_size;
+    ASN1_INTEGER_SET* digest;
+    ASN1_INTEGER_SET* padding;
+    ASN1_INTEGER* ec_curve;
+    ASN1_INTEGER* rsa_public_exponent;
+    ASN1_INTEGER_SET* mgf_digest;
+    ASN1_NULL* rollback_resistance;
+    ASN1_NULL* early_boot_only;
+    ASN1_INTEGER* active_date_time;
+    ASN1_INTEGER* origination_expire_date_time;
+    ASN1_INTEGER* usage_expire_date_time;
+    ASN1_INTEGER* usage_count_limit;
+    ASN1_NULL* no_auth_required;
+    ASN1_INTEGER* user_auth_type;
+    ASN1_INTEGER* auth_timeout;
+    ASN1_NULL* allow_while_on_body;
+    ASN1_NULL* trusted_user_presence_required;
+    ASN1_NULL* trusted_confirmation_required;
+    ASN1_NULL* unlocked_device_required;
+    ASN1_INTEGER* creation_date_time;
+    ASN1_INTEGER* origin;
+    KM_ROOT_OF_TRUST* root_of_trust;
+    ASN1_INTEGER* os_version;
+    ASN1_INTEGER* os_patchlevel;
+    ASN1_OCTET_STRING* attestation_application_id;
+    ASN1_OCTET_STRING* attestation_id_brand;
+    ASN1_OCTET_STRING* attestation_id_device;
+    ASN1_OCTET_STRING* attestation_id_product;
+    ASN1_OCTET_STRING* attestation_id_serial;
+    ASN1_OCTET_STRING* attestation_id_imei;
+    ASN1_OCTET_STRING* attestation_id_meid;
+    ASN1_OCTET_STRING* attestation_id_manufacturer;
+    ASN1_OCTET_STRING* attestation_id_model;
+    ASN1_INTEGER* vendor_patchlevel;
+    ASN1_INTEGER* boot_patchlevel;
+    ASN1_NULL* device_unique_attestation;
+    ASN1_NULL* identity_credential;
+} KM_AUTH_LIST;
+
+ASN1_SEQUENCE(KM_AUTH_LIST) = {
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, purpose, ASN1_INTEGER, TAG_PURPOSE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, algorithm, ASN1_INTEGER, TAG_ALGORITHM.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, key_size, ASN1_INTEGER, TAG_KEY_SIZE.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, digest, ASN1_INTEGER, TAG_DIGEST.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, padding, ASN1_INTEGER, TAG_PADDING.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, ec_curve, ASN1_INTEGER, TAG_EC_CURVE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rsa_public_exponent, ASN1_INTEGER,
+                     TAG_RSA_PUBLIC_EXPONENT.maskedTag()),
+        ASN1_EXP_SET_OF_OPT(KM_AUTH_LIST, mgf_digest, ASN1_INTEGER,
+                            TAG_RSA_OAEP_MGF_DIGEST.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, rollback_resistance, ASN1_NULL,
+                     TAG_ROLLBACK_RESISTANCE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, early_boot_only, ASN1_NULL, TAG_EARLY_BOOT_ONLY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, active_date_time, ASN1_INTEGER, TAG_ACTIVE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origination_expire_date_time, ASN1_INTEGER,
+                     TAG_ORIGINATION_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_expire_date_time, ASN1_INTEGER,
+                     TAG_USAGE_EXPIRE_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, usage_count_limit, ASN1_INTEGER,
+                     TAG_USAGE_COUNT_LIMIT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, no_auth_required, ASN1_NULL, TAG_NO_AUTH_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, user_auth_type, ASN1_INTEGER, TAG_USER_AUTH_TYPE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, auth_timeout, ASN1_INTEGER, TAG_AUTH_TIMEOUT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, allow_while_on_body, ASN1_NULL,
+                     TAG_ALLOW_WHILE_ON_BODY.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_user_presence_required, ASN1_NULL,
+                     TAG_TRUSTED_USER_PRESENCE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, trusted_confirmation_required, ASN1_NULL,
+                     TAG_TRUSTED_CONFIRMATION_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, unlocked_device_required, ASN1_NULL,
+                     TAG_UNLOCKED_DEVICE_REQUIRED.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, creation_date_time, ASN1_INTEGER,
+                     TAG_CREATION_DATETIME.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, origin, ASN1_INTEGER, TAG_ORIGIN.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, root_of_trust, KM_ROOT_OF_TRUST, TAG_ROOT_OF_TRUST.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_version, ASN1_INTEGER, TAG_OS_VERSION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, os_patchlevel, ASN1_INTEGER, TAG_OS_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_application_id, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_APPLICATION_ID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_brand, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_BRAND.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_device, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_DEVICE.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_product, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_PRODUCT.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_serial, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_SERIAL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_imei, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_IMEI.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_meid, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MEID.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_manufacturer, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MANUFACTURER.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, attestation_id_model, ASN1_OCTET_STRING,
+                     TAG_ATTESTATION_ID_MODEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, vendor_patchlevel, ASN1_INTEGER,
+                     TAG_VENDOR_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, boot_patchlevel, ASN1_INTEGER, TAG_BOOT_PATCHLEVEL.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, device_unique_attestation, ASN1_NULL,
+                     TAG_DEVICE_UNIQUE_ATTESTATION.maskedTag()),
+        ASN1_EXP_OPT(KM_AUTH_LIST, identity_credential, ASN1_NULL,
+                     TAG_IDENTITY_CREDENTIAL_KEY.maskedTag()),
+} ASN1_SEQUENCE_END(KM_AUTH_LIST);
+IMPLEMENT_ASN1_FUNCTIONS(KM_AUTH_LIST);
+
+typedef struct km_key_description {
+    ASN1_INTEGER* attestation_version;
+    ASN1_ENUMERATED* attestation_security_level;
+    ASN1_INTEGER* keymint_version;
+    ASN1_ENUMERATED* keymint_security_level;
+    ASN1_OCTET_STRING* attestation_challenge;
+    ASN1_INTEGER* unique_id;
+    KM_AUTH_LIST* software_enforced;
+    KM_AUTH_LIST* tee_enforced;
+} KM_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(KM_KEY_DESCRIPTION) = {
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymint_version, ASN1_INTEGER),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, keymint_security_level, ASN1_ENUMERATED),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, attestation_challenge, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, unique_id, ASN1_OCTET_STRING),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, software_enforced, KM_AUTH_LIST),
+        ASN1_SIMPLE(KM_KEY_DESCRIPTION, tee_enforced, KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(KM_KEY_DESCRIPTION);
+IMPLEMENT_ASN1_FUNCTIONS(KM_KEY_DESCRIPTION);
+
+template <Tag tag>
+void copyAuthTag(const stack_st_ASN1_INTEGER* stack, TypedTag<TagType::ENUM_REP, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    for (size_t i = 0; i < sk_ASN1_INTEGER_num(stack); ++i) {
+        auth_list->push_back(
+                ttag, static_cast<ValueT>(ASN1_INTEGER_get(sk_ASN1_INTEGER_value(stack, i))));
+    }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ENUM, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    typedef typename TypedTag2ValueType<decltype(ttag)>::type ValueT;
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, static_cast<ValueT>(ASN1_INTEGER_get(asn1_int)));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::UINT, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    auth_list->push_back(ttag, ASN1_INTEGER_get(asn1_int));
+}
+
+BIGNUM* construct_uint_max() {
+    BIGNUM* value = BN_new();
+    BIGNUM_Ptr one(BN_new());
+    BN_one(one.get());
+    BN_lshift(value, one.get(), 32);
+    return value;
+}
+
+uint64_t BignumToUint64(BIGNUM* num) {
+    static_assert((sizeof(BN_ULONG) == sizeof(uint32_t)) || (sizeof(BN_ULONG) == sizeof(uint64_t)),
+                  "This implementation only supports 32 and 64-bit BN_ULONG");
+    if (sizeof(BN_ULONG) == sizeof(uint32_t)) {
+        BIGNUM_Ptr uint_max(construct_uint_max());
+        BIGNUM_Ptr hi(BN_new()), lo(BN_new());
+        BN_CTX_Ptr ctx(BN_CTX_new());
+        BN_div(hi.get(), lo.get(), num, uint_max.get(), ctx.get());
+        return static_cast<uint64_t>(BN_get_word(hi.get())) << 32 | BN_get_word(lo.get());
+    } else if (sizeof(BN_ULONG) == sizeof(uint64_t)) {
+        return BN_get_word(num);
+    } else {
+        return 0;
+    }
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::ULONG, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_INTEGER* asn1_int, TypedTag<TagType::DATE, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_int) return;
+    BIGNUM_Ptr num(ASN1_INTEGER_to_BN(asn1_int, nullptr));
+    auth_list->push_back(ttag, BignumToUint64(num.get()));
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_NULL* asn1_null, TypedTag<TagType::BOOL, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_null) return;
+    auth_list->push_back(ttag);
+}
+
+template <Tag tag>
+void copyAuthTag(const ASN1_OCTET_STRING* asn1_string, TypedTag<TagType::BYTES, tag> ttag,
+                 AuthorizationSet* auth_list) {
+    if (!asn1_string) return;
+    vector<uint8_t> buf(asn1_string->data, asn1_string->data + asn1_string->length);
+    auth_list->push_back(ttag, buf);
+}
+
+// Extract the values from the specified ASN.1 record and place them in auth_list.
+// Does nothing with root-of-trust field.
+static ErrorCode extract_auth_list(const KM_AUTH_LIST* record, AuthorizationSet* auth_list) {
+    if (!record) return ErrorCode::OK;
+
+    // Fields ordered in tag order.
+    copyAuthTag(record->purpose, TAG_PURPOSE, auth_list);
+    copyAuthTag(record->algorithm, TAG_ALGORITHM, auth_list);
+    copyAuthTag(record->key_size, TAG_KEY_SIZE, auth_list);
+    copyAuthTag(record->digest, TAG_DIGEST, auth_list);
+    copyAuthTag(record->padding, TAG_PADDING, auth_list);
+    copyAuthTag(record->ec_curve, TAG_EC_CURVE, auth_list);
+    copyAuthTag(record->rsa_public_exponent, TAG_RSA_PUBLIC_EXPONENT, auth_list);
+    copyAuthTag(record->mgf_digest, TAG_RSA_OAEP_MGF_DIGEST, auth_list);
+    copyAuthTag(record->rollback_resistance, TAG_ROLLBACK_RESISTANCE, auth_list);
+    copyAuthTag(record->early_boot_only, TAG_EARLY_BOOT_ONLY, auth_list);
+    copyAuthTag(record->active_date_time, TAG_ACTIVE_DATETIME, auth_list);
+    copyAuthTag(record->origination_expire_date_time, TAG_ORIGINATION_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->usage_expire_date_time, TAG_USAGE_EXPIRE_DATETIME, auth_list);
+    copyAuthTag(record->usage_count_limit, TAG_USAGE_COUNT_LIMIT, auth_list);
+    copyAuthTag(record->no_auth_required, TAG_NO_AUTH_REQUIRED, auth_list);
+    copyAuthTag(record->user_auth_type, TAG_USER_AUTH_TYPE, auth_list);
+    copyAuthTag(record->auth_timeout, TAG_AUTH_TIMEOUT, auth_list);
+    copyAuthTag(record->allow_while_on_body, TAG_ALLOW_WHILE_ON_BODY, auth_list);
+    copyAuthTag(record->trusted_user_presence_required, TAG_TRUSTED_USER_PRESENCE_REQUIRED,
+                auth_list);
+    copyAuthTag(record->trusted_confirmation_required, TAG_TRUSTED_CONFIRMATION_REQUIRED,
+                auth_list);
+    copyAuthTag(record->unlocked_device_required, TAG_UNLOCKED_DEVICE_REQUIRED, auth_list);
+    copyAuthTag(record->creation_date_time, TAG_CREATION_DATETIME, auth_list);
+    copyAuthTag(record->origin, TAG_ORIGIN, auth_list);
+    // root_of_trust dealt with separately
+    copyAuthTag(record->os_version, TAG_OS_VERSION, auth_list);
+    copyAuthTag(record->os_patchlevel, TAG_OS_PATCHLEVEL, auth_list);
+    copyAuthTag(record->attestation_application_id, TAG_ATTESTATION_APPLICATION_ID, auth_list);
+    copyAuthTag(record->attestation_id_brand, TAG_ATTESTATION_ID_BRAND, auth_list);
+    copyAuthTag(record->attestation_id_device, TAG_ATTESTATION_ID_DEVICE, auth_list);
+    copyAuthTag(record->attestation_id_product, TAG_ATTESTATION_ID_PRODUCT, auth_list);
+    copyAuthTag(record->attestation_id_serial, TAG_ATTESTATION_ID_SERIAL, auth_list);
+    copyAuthTag(record->attestation_id_imei, TAG_ATTESTATION_ID_IMEI, auth_list);
+    copyAuthTag(record->attestation_id_meid, TAG_ATTESTATION_ID_MEID, auth_list);
+    copyAuthTag(record->attestation_id_manufacturer, TAG_ATTESTATION_ID_MANUFACTURER, auth_list);
+    copyAuthTag(record->attestation_id_model, TAG_ATTESTATION_ID_MODEL, auth_list);
+    copyAuthTag(record->vendor_patchlevel, TAG_VENDOR_PATCHLEVEL, auth_list);
+    copyAuthTag(record->boot_patchlevel, TAG_BOOT_PATCHLEVEL, auth_list);
+    copyAuthTag(record->device_unique_attestation, TAG_DEVICE_UNIQUE_ATTESTATION, auth_list);
+    copyAuthTag(record->identity_credential, TAG_IDENTITY_CREDENTIAL_KEY, auth_list);
+
+    return ErrorCode::OK;
+}
+
+MAKE_OPENSSL_PTR_TYPE(KM_KEY_DESCRIPTION)
+
+// Parse the DER-encoded attestation record, placing the results in keymint_version,
+// attestation_challenge, software_enforced, tee_enforced and unique_id.
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                                   uint32_t* attestation_version,  //
+                                   SecurityLevel* attestation_security_level,
+                                   uint32_t* keymint_version, SecurityLevel* keymint_security_level,
+                                   vector<uint8_t>* attestation_challenge,
+                                   AuthorizationSet* software_enforced,
+                                   AuthorizationSet* tee_enforced,  //
+                                   vector<uint8_t>* unique_id) {
+    const uint8_t* p = asn1_key_desc;
+    KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+    if (!record.get()) return ErrorCode::UNKNOWN_ERROR;
+
+    *attestation_version = ASN1_INTEGER_get(record->attestation_version);
+    *attestation_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->attestation_security_level));
+    *keymint_version = ASN1_INTEGER_get(record->keymint_version);
+    *keymint_security_level =
+            static_cast<SecurityLevel>(ASN1_ENUMERATED_get(record->keymint_security_level));
+
+    auto& chall = record->attestation_challenge;
+    attestation_challenge->resize(chall->length);
+    memcpy(attestation_challenge->data(), chall->data, chall->length);
+    auto& uid = record->unique_id;
+    unique_id->resize(uid->length);
+    memcpy(unique_id->data(), uid->data, uid->length);
+
+    ErrorCode error = extract_auth_list(record->software_enforced, software_enforced);
+    if (error != ErrorCode::OK) return error;
+
+    return extract_auth_list(record->tee_enforced, tee_enforced);
+}
+
+ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                              vector<uint8_t>* verified_boot_key, VerifiedBoot* verified_boot_state,
+                              bool* device_locked, vector<uint8_t>* verified_boot_hash) {
+    if (!verified_boot_key || !verified_boot_state || !device_locked || !verified_boot_hash) {
+        LOG(ERROR) << AT << "null pointer input(s)";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    const uint8_t* p = asn1_key_desc;
+    KM_KEY_DESCRIPTION_Ptr record(d2i_KM_KEY_DESCRIPTION(nullptr, &p, asn1_key_desc_len));
+    if (!record.get()) {
+        LOG(ERROR) << AT << "Failed record parsing";
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    KM_ROOT_OF_TRUST* root_of_trust = nullptr;
+    if (record->tee_enforced && record->tee_enforced->root_of_trust) {
+        root_of_trust = record->tee_enforced->root_of_trust;
+    } else if (record->software_enforced && record->software_enforced->root_of_trust) {
+        root_of_trust = record->software_enforced->root_of_trust;
+    } else {
+        LOG(ERROR) << AT << " Failed root of trust parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    if (!root_of_trust->verified_boot_key) {
+        LOG(ERROR) << AT << " Failed verified boot key parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    auto& vb_key = root_of_trust->verified_boot_key;
+    verified_boot_key->resize(vb_key->length);
+    memcpy(verified_boot_key->data(), vb_key->data, vb_key->length);
+
+    *verified_boot_state =
+            static_cast<VerifiedBoot>(ASN1_ENUMERATED_get(root_of_trust->verified_boot_state));
+    if (!verified_boot_state) {
+        LOG(ERROR) << AT << " Failed verified boot state parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    *device_locked = root_of_trust->device_locked;
+    if (!device_locked) {
+        LOG(ERROR) << AT << " Failed device locked parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+
+    auto& vb_hash = root_of_trust->verified_boot_hash;
+    if (!vb_hash) {
+        LOG(ERROR) << AT << " Failed verified boot hash parsing";
+        return ErrorCode::INVALID_ARGUMENT;
+    }
+    verified_boot_hash->resize(vb_hash->length);
+    memcpy(verified_boot_hash->data(), vb_hash->data, vb_hash->length);
+    return ErrorCode::OK;  // KM_ERROR_OK;
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/authorization_set.cpp b/security/keymint/support/authorization_set.cpp
new file mode 100644
index 0000000..25eace3
--- /dev/null
+++ b/security/keymint/support/authorization_set.cpp
@@ -0,0 +1,258 @@
+/*
+ * Copyright 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 <keymint_support/authorization_set.h>
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+void AuthorizationSet::Sort() {
+    std::sort(data_.begin(), data_.end());
+}
+
+void AuthorizationSet::Deduplicate() {
+    if (data_.empty()) return;
+
+    Sort();
+    std::vector<KeyParameter> result;
+
+    auto curr = data_.begin();
+    auto prev = curr++;
+    for (; curr != data_.end(); ++prev, ++curr) {
+        if (prev->tag == Tag::INVALID) continue;
+
+        if (*prev != *curr) {
+            result.push_back(std::move(*prev));
+        }
+    }
+    result.push_back(std::move(*prev));
+
+    std::swap(data_, result);
+}
+
+void AuthorizationSet::Union(const AuthorizationSet& other) {
+    data_.insert(data_.end(), other.data_.begin(), other.data_.end());
+    Deduplicate();
+}
+
+void AuthorizationSet::Subtract(const AuthorizationSet& other) {
+    Deduplicate();
+
+    auto i = other.begin();
+    while (i != other.end()) {
+        int pos = -1;
+        do {
+            pos = find(i->tag, pos);
+            if (pos != -1 && (*i == data_[pos])) {
+                data_.erase(data_.begin() + pos);
+                break;
+            }
+        } while (pos != -1);
+        ++i;
+    }
+}
+
+KeyParameter& AuthorizationSet::operator[](int at) {
+    return data_[at];
+}
+
+const KeyParameter& AuthorizationSet::operator[](int at) const {
+    return data_[at];
+}
+
+void AuthorizationSet::Clear() {
+    data_.clear();
+}
+
+size_t AuthorizationSet::GetTagCount(Tag tag) const {
+    size_t count = 0;
+    for (int pos = -1; (pos = find(tag, pos)) != -1;) ++count;
+    return count;
+}
+
+int AuthorizationSet::find(Tag tag, int begin) const {
+    auto iter = data_.begin() + (1 + begin);
+
+    while (iter != data_.end() && iter->tag != tag) ++iter;
+
+    if (iter != data_.end()) return iter - data_.begin();
+    return -1;
+}
+
+bool AuthorizationSet::erase(int index) {
+    auto pos = data_.begin() + index;
+    if (pos != data_.end()) {
+        data_.erase(pos);
+        return true;
+    }
+    return false;
+}
+
+std::optional<std::reference_wrapper<const KeyParameter>> AuthorizationSet::GetEntry(
+        Tag tag) const {
+    int pos = find(tag);
+    if (pos == -1) return {};
+    return std::reference_wrapper(data_[pos]);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaKey(uint32_t key_size,
+                                                         uint64_t public_exponent) {
+    Authorization(TAG_ALGORITHM, Algorithm::RSA);
+    Authorization(TAG_KEY_SIZE, key_size);
+    Authorization(TAG_RSA_PUBLIC_EXPONENT, public_exponent);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::EC);
+    Authorization(TAG_KEY_SIZE, key_size);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaKey(EcCurve curve) {
+    Authorization(TAG_ALGORITHM, Algorithm::EC);
+    Authorization(TAG_EC_CURVE, curve);
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::AES);
+    return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::TRIPLE_DES);
+    return Authorization(TAG_KEY_SIZE, key_size);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::HmacKey(uint32_t key_size) {
+    Authorization(TAG_ALGORITHM, Algorithm::HMAC);
+    Authorization(TAG_KEY_SIZE, key_size);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaSigningKey(uint32_t key_size,
+                                                                uint64_t public_exponent) {
+    RsaKey(key_size, public_exponent);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::RsaEncryptionKey(uint32_t key_size,
+                                                                   uint64_t public_exponent) {
+    RsaKey(key_size, public_exponent);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(uint32_t key_size) {
+    EcdsaKey(key_size);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcdsaSigningKey(EcCurve curve) {
+    EcdsaKey(curve);
+    return SigningKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AesEncryptionKey(uint32_t key_size) {
+    AesKey(key_size);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::TripleDesEncryptionKey(uint32_t key_size) {
+    TripleDesKey(key_size);
+    return EncryptionKey();
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::SigningKey() {
+    Authorization(TAG_PURPOSE, KeyPurpose::SIGN);
+    return Authorization(TAG_PURPOSE, KeyPurpose::VERIFY);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EncryptionKey() {
+    Authorization(TAG_PURPOSE, KeyPurpose::ENCRYPT);
+    return Authorization(TAG_PURPOSE, KeyPurpose::DECRYPT);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::AttestKey() {
+    return Authorization(TAG_PURPOSE, KeyPurpose::ATTEST_KEY);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::NoDigestOrPadding() {
+    Authorization(TAG_DIGEST, Digest::NONE);
+    return Authorization(TAG_PADDING, PaddingMode::NONE);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::EcbMode() {
+    return Authorization(TAG_BLOCK_MODE, BlockMode::ECB);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMinMacLen(uint32_t minMacLength) {
+    return BlockMode(BlockMode::GCM)
+            .Padding(PaddingMode::NONE)
+            .Authorization(TAG_MIN_MAC_LENGTH, minMacLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::GcmModeMacLen(uint32_t macLength) {
+    return BlockMode(BlockMode::GCM)
+            .Padding(PaddingMode::NONE)
+            .Authorization(TAG_MAC_LENGTH, macLength);
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::BlockMode(
+        std::initializer_list<aidl::android::hardware::security::keymint::BlockMode> blockModes) {
+    for (auto mode : blockModes) {
+        push_back(TAG_BLOCK_MODE, mode);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Digest(std::vector<keymint::Digest> digests) {
+    for (auto digest : digests) {
+        push_back(TAG_DIGEST, digest);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::OaepMGFDigest(
+        const std::vector<android::hardware::security::keymint::Digest>& digests) {
+    for (auto digest : digests) {
+        push_back(TAG_RSA_OAEP_MGF_DIGEST, digest);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::Padding(
+        std::initializer_list<PaddingMode> paddingModes) {
+    for (auto paddingMode : paddingModes) {
+        push_back(TAG_PADDING, paddingMode);
+    }
+    return *this;
+}
+
+AuthorizationSetBuilder& AuthorizationSetBuilder::SetDefaultValidity() {
+    // Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to
+    // GeneralizedTime 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
+    constexpr uint64_t kUndefinedExpirationDateTime = 253402300799000;
+    Authorization(TAG_CERTIFICATE_NOT_BEFORE, 0);
+    return Authorization(TAG_CERTIFICATE_NOT_AFTER, kUndefinedExpirationDateTime);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/attestation_record.h b/security/keymint/support/include/keymint_support/attestation_record.h
new file mode 100644
index 0000000..bc76c93
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/attestation_record.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/IKeyMintDevice.h>
+
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/authorization_set.h>
+#include <keymint_support/openssl_utils.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+class AuthorizationSet;
+
+/**
+ * The OID for Android attestation records.  For the curious, it breaks down as follows:
+ *
+ * 1 = ISO
+ * 3 = org
+ * 6 = DoD (Huh? OIDs are weird.)
+ * 1 = IANA
+ * 4 = Private
+ * 1 = Enterprises
+ * 11129 = Google
+ * 2 = Google security
+ * 1 = certificate extension
+ * 17 = Android attestation extension.
+ */
+static const char kAttestionRecordOid[] = "1.3.6.1.4.1.11129.2.1.17";
+
+enum class VerifiedBoot : uint8_t {
+    VERIFIED = 0,
+    SELF_SIGNED = 1,
+    UNVERIFIED = 2,
+    FAILED = 3,
+};
+
+struct RootOfTrust {
+    SecurityLevel security_level;
+    vector<uint8_t> verified_boot_key;
+    vector<uint8_t> verified_boot_hash;
+    VerifiedBoot verified_boot_state;
+    bool device_locked;
+};
+
+struct AttestationRecord {
+    RootOfTrust root_of_trust;
+    uint32_t attestation_version;
+    SecurityLevel attestation_security_level;
+    uint32_t keymint_version;
+    SecurityLevel keymint_security_level;
+    std::vector<uint8_t> attestation_challenge;
+    AuthorizationSet software_enforced;
+    AuthorizationSet hardware_enforced;
+    std::vector<uint8_t> unique_id;
+};
+
+ErrorCode parse_attestation_record(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                                   uint32_t* attestation_version,  //
+                                   SecurityLevel* attestation_security_level,
+                                   uint32_t* keymint_version, SecurityLevel* keymint_security_level,
+                                   std::vector<uint8_t>* attestation_challenge,
+                                   AuthorizationSet* software_enforced,
+                                   AuthorizationSet* tee_enforced,  //
+                                   std::vector<uint8_t>* unique_id);
+
+ErrorCode parse_root_of_trust(const uint8_t* asn1_key_desc, size_t asn1_key_desc_len,
+                              std::vector<uint8_t>* verified_boot_key,
+                              VerifiedBoot* verified_boot_state, bool* device_locked,
+                              std::vector<uint8_t>* verified_boot_hash);
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/authorization_set.h b/security/keymint/support/include/keymint_support/authorization_set.h
new file mode 100644
index 0000000..ca51b08
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/authorization_set.h
@@ -0,0 +1,334 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+
+#include <keymint_support/keymint_tags.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using std::vector;
+
+class AuthorizationSetBuilder;
+
+/**
+ * A collection of KeyParameters. It provides memory ownership and some convenient functionality for
+ * sorting, deduplicating, joining, and subtracting sets of KeyParameters.
+ */
+class AuthorizationSet {
+  public:
+    typedef KeyParameter value_type;
+
+    /**
+     * Construct an empty, dynamically-allocated, growable AuthorizationSet.
+     */
+    AuthorizationSet(){};
+
+    // Copy constructor.
+    AuthorizationSet(const AuthorizationSet& other) : data_(other.data_) {}
+
+    // Move constructor.
+    AuthorizationSet(AuthorizationSet&& other) noexcept : data_(std::move(other.data_)) {}
+
+    // Constructor from vector<KeyParameter>
+    AuthorizationSet(const vector<KeyParameter>& other) { *this = other; }
+
+    // Copy assignment.
+    AuthorizationSet& operator=(const AuthorizationSet& other) {
+        data_ = other.data_;
+        return *this;
+    }
+
+    // Move assignment.
+    AuthorizationSet& operator=(AuthorizationSet&& other) noexcept {
+        data_ = std::move(other.data_);
+        return *this;
+    }
+
+    AuthorizationSet& operator=(const vector<KeyParameter>& other) {
+        if (other.size() > 0) {
+            data_.resize(other.size());
+            for (size_t i = 0; i < data_.size(); ++i) {
+                /* This makes a deep copy even of embedded blobs.
+                 * See assignment operator/copy constructor of vector.*/
+                data_[i] = other[i];
+            }
+        }
+        return *this;
+    }
+
+    /**
+     * Clear existing authorization set data
+     */
+    void Clear();
+
+    ~AuthorizationSet() = default;
+
+    /**
+     * Returns the size of the set.
+     */
+    size_t size() const { return data_.size(); }
+
+    /**
+     * Returns true if the set is empty.
+     */
+    bool empty() const { return size() == 0; }
+
+    /**
+     * Returns the data in the set, directly. Be careful with this.
+     */
+    const KeyParameter* data() const { return data_.data(); }
+
+    /**
+     * Sorts the set
+     */
+    void Sort();
+
+    /**
+     * Sorts the set and removes duplicates (inadvertently duplicating tags is easy to do with the
+     * AuthorizationSetBuilder).
+     */
+    void Deduplicate();
+
+    /**
+     * Adds all elements from \p set that are not already present in this AuthorizationSet.  As a
+     * side-effect, if \p set is not null this AuthorizationSet will end up sorted.
+     */
+    void Union(const AuthorizationSet& set);
+
+    /**
+     * Removes all elements in \p set from this AuthorizationSet.
+     */
+    void Subtract(const AuthorizationSet& set);
+
+    /**
+     * Returns the offset of the next entry that matches \p tag, starting from the element after \p
+     * begin.  If not found, returns -1.
+     */
+    int find(Tag tag, int begin = -1) const;
+
+    /**
+     * Removes the entry at the specified index. Returns true if successful, false if the index was
+     * out of bounds.
+     */
+    bool erase(int index);
+
+    /**
+     * Returns iterator (pointer) to beginning of elems array, to enable STL-style iteration
+     */
+    auto begin() { return data_.begin(); }
+    auto begin() const { return data_.begin(); }
+
+    /**
+     * Returns iterator (pointer) one past end of elems array, to enable STL-style iteration
+     */
+    auto end() { return data_.end(); }
+    auto end() const { return data_.end(); }
+
+    /**
+     * Returns the nth element of the set.
+     * Like for std::vector::operator[] there is no range check performed. Use of out of range
+     * indices is undefined.
+     */
+    KeyParameter& operator[](int n);
+
+    /**
+     * Returns the nth element of the set.
+     * Like for std::vector::operator[] there is no range check performed. Use of out of range
+     * indices is undefined.
+     */
+    const KeyParameter& operator[](int n) const;
+
+    /**
+     * Returns true if the set contains at least one instance of \p tag
+     */
+    bool Contains(Tag tag) const { return find(tag) != -1; }
+
+    template <TagType tag_type, Tag tag, typename ValueT>
+    bool Contains(TypedTag<tag_type, tag> ttag, const ValueT& value) const {
+        for (const auto& param : data_) {
+            auto entry = authorizationValue(ttag, param);
+            if (entry && static_cast<ValueT>(*entry) == value) return true;
+        }
+        return false;
+    }
+    /**
+     * Returns the number of \p tag entries.
+     */
+    size_t GetTagCount(Tag tag) const;
+
+    template <typename T>
+    inline auto GetTagValue(T tag) const -> decltype(authorizationValue(tag, KeyParameter())) {
+        auto entry = GetEntry(tag);
+        if (entry) return authorizationValue(tag, *entry);
+        return {};
+    }
+
+    void push_back(const KeyParameter& param) { data_.push_back(param); }
+    void push_back(KeyParameter&& param) { data_.push_back(std::move(param)); }
+    void push_back(const AuthorizationSet& set) {
+        for (auto& entry : set) {
+            push_back(entry);
+        }
+    }
+    void push_back(AuthorizationSet&& set) {
+        std::move(set.begin(), set.end(), std::back_inserter(*this));
+    }
+
+    /**
+     * Append the tag and enumerated value to the set.
+     * "val" may be exactly one parameter unless a boolean parameter is added.
+     * In this case "val" is omitted. This condition is checked at compile time by Authorization()
+     */
+    template <typename TypedTagT, typename... Value>
+    void push_back(TypedTagT tag, Value&&... val) {
+        push_back(Authorization(tag, std::forward<Value>(val)...));
+    }
+
+    template <typename Iterator>
+    void append(Iterator begin, Iterator end) {
+        while (begin != end) {
+            push_back(*begin);
+            ++begin;
+        }
+    }
+
+    vector<KeyParameter> vector_data() const {
+        vector<KeyParameter> result(begin(), end());
+        return result;
+    }
+
+  private:
+    std::optional<std::reference_wrapper<const KeyParameter>> GetEntry(Tag tag) const;
+
+    std::vector<KeyParameter> data_;
+};
+
+class AuthorizationSetBuilder : public AuthorizationSet {
+  public:
+    template <typename TagType, typename... ValueType>
+    AuthorizationSetBuilder& Authorization(TagType ttag, ValueType&&... value) {
+        push_back(ttag, std::forward<ValueType>(value)...);
+        return *this;
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const uint8_t* data,
+                                           size_t data_length) {
+        vector<uint8_t> new_blob(data, data + data_length);
+        push_back(ttag, new_blob);
+        return *this;
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, const char* data,
+                                           size_t data_length) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data), data_length);
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag, char* data,
+                                           size_t data_length) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data), data_length);
+    }
+
+    template <Tag tag, size_t size>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag,
+                                           const char (&data)[size]) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(&data[0]),
+                             size - 1);  // drop the terminating '\0'
+    }
+
+    template <Tag tag>
+    AuthorizationSetBuilder& Authorization(TypedTag<TagType::BYTES, tag> ttag,
+                                           const std::string& data) {
+        return Authorization(ttag, reinterpret_cast<const uint8_t*>(data.data()), data.size());
+    }
+
+    AuthorizationSetBuilder& Authorizations(const AuthorizationSet& set) {
+        for (const auto& entry : set) {
+            push_back(entry);
+        }
+        return *this;
+    }
+
+    AuthorizationSetBuilder& RsaKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& EcdsaKey(uint32_t key_size);
+    AuthorizationSetBuilder& EcdsaKey(EcCurve curve);
+    AuthorizationSetBuilder& AesKey(uint32_t key_size);
+    AuthorizationSetBuilder& TripleDesKey(uint32_t key_size);
+    AuthorizationSetBuilder& HmacKey(uint32_t key_size);
+
+    AuthorizationSetBuilder& RsaSigningKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& RsaEncryptionKey(uint32_t key_size, uint64_t public_exponent);
+    AuthorizationSetBuilder& EcdsaSigningKey(uint32_t key_size);
+    AuthorizationSetBuilder& EcdsaSigningKey(EcCurve curve);
+    AuthorizationSetBuilder& AesEncryptionKey(uint32_t key_size);
+    AuthorizationSetBuilder& TripleDesEncryptionKey(uint32_t key_size);
+
+    AuthorizationSetBuilder& SigningKey();
+    AuthorizationSetBuilder& EncryptionKey();
+    AuthorizationSetBuilder& AttestKey();
+
+    AuthorizationSetBuilder& NoDigestOrPadding();
+
+    AuthorizationSetBuilder& EcbMode();
+    AuthorizationSetBuilder& GcmModeMinMacLen(uint32_t minMacLength);
+    AuthorizationSetBuilder& GcmModeMacLen(uint32_t macLength);
+
+    AuthorizationSetBuilder& BlockMode(std::initializer_list<BlockMode> blockModes);
+    AuthorizationSetBuilder& OaepMGFDigest(const std::vector<Digest>& digests);
+    AuthorizationSetBuilder& Digest(std::vector<Digest> digests);
+    AuthorizationSetBuilder& Padding(std::initializer_list<PaddingMode> paddings);
+
+    AuthorizationSetBuilder& SetDefaultValidity();
+
+    AuthorizationSetBuilder& AttestationChallenge(const std::string& challenge) {
+        return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
+    }
+    AuthorizationSetBuilder& AttestationChallenge(std::vector<uint8_t> challenge) {
+        return Authorization(TAG_ATTESTATION_CHALLENGE, challenge);
+    }
+
+    AuthorizationSetBuilder& AttestationApplicationId(const std::string& id) {
+        return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
+    }
+    AuthorizationSetBuilder& AttestationApplicationId(std::vector<uint8_t> id) {
+        return Authorization(TAG_ATTESTATION_APPLICATION_ID, id);
+    }
+
+    template <typename... T>
+    AuthorizationSetBuilder& BlockMode(T&&... a) {
+        return BlockMode({std::forward<T>(a)...});
+    }
+    template <typename... T>
+    AuthorizationSetBuilder& Digest(T&&... a) {
+        return Digest({std::forward<T>(a)...});
+    }
+    template <typename... T>
+    AuthorizationSetBuilder& Padding(T&&... a) {
+        return Padding({std::forward<T>(a)...});
+    }
+};
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/key_param_output.h b/security/keymint/support/include/keymint_support/key_param_output.h
new file mode 100644
index 0000000..c2b0029
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/key_param_output.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.
+ */
+
+#pragma once
+
+#include <iostream>
+#include <vector>
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <aidl/android/hardware/security/keymint/KeyCharacteristics.h>
+#include <aidl/android/hardware/security/keymint/KeyOrigin.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
+
+#include "keymint_tags.h"
+
+namespace aidl::android::hardware::security::keymint {
+
+inline ::std::ostream& operator<<(::std::ostream& os, Algorithm value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, BlockMode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, Digest value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, EcCurve value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, ErrorCode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, KeyOrigin value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, PaddingMode value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, SecurityLevel value) {
+    return os << toString(value);
+}
+
+template <typename ValueT>
+::std::ostream& operator<<(::std::ostream& os, const std::optional<ValueT>& value) {
+    if (!value.isOk()) {
+        os << "(value not present)";
+    } else {
+        os << value.value();
+    }
+    return os;
+}
+
+::std::ostream& operator<<(::std::ostream& os, const ::std::vector<KeyParameter>& set);
+::std::ostream& operator<<(::std::ostream& os, const KeyParameter& param);
+
+inline ::std::ostream& operator<<(::std::ostream& os, const KeyCharacteristics& value) {
+    for (auto& entry : value.authorizations) {
+        os << value.securityLevel << ": " << entry;
+    }
+    return os;
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, KeyPurpose value) {
+    return os << toString(value);
+}
+
+inline ::std::ostream& operator<<(::std::ostream& os, Tag tag) {
+    return os << toString(tag);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/keymint_tags.h b/security/keymint/support/include/keymint_support/keymint_tags.h
new file mode 100644
index 0000000..5b2a6f3
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/keymint_tags.h
@@ -0,0 +1,340 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/Algorithm.h>
+#include <aidl/android/hardware/security/keymint/BlockMode.h>
+#include <aidl/android/hardware/security/keymint/Digest.h>
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
+#include <aidl/android/hardware/security/keymint/HardwareAuthenticatorType.h>
+#include <aidl/android/hardware/security/keymint/KeyOrigin.h>
+#include <aidl/android/hardware/security/keymint/KeyParameter.h>
+#include <aidl/android/hardware/security/keymint/KeyPurpose.h>
+#include <aidl/android/hardware/security/keymint/PaddingMode.h>
+#include <aidl/android/hardware/security/keymint/SecurityLevel.h>
+#include <aidl/android/hardware/security/keymint/Tag.h>
+#include <aidl/android/hardware/security/keymint/TagType.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+constexpr TagType typeFromTag(Tag tag) {
+    return static_cast<TagType>(static_cast<uint32_t>(tag) & static_cast<uint32_t>(0xf0000000));
+}
+
+/**
+ * TypedTag is a templatized version of Tag, which provides compile-time checking of KeyMint tag
+ * types.  Instances are convertible to Tag, so they can be used wherever Tag is expected, and
+ * because they encode the tag type it's possible to create function overloads that only operate on
+ * tags with a particular type.
+ */
+template <TagType tag_type, Tag tag>
+struct TypedTag {
+    inline TypedTag() {
+        // Ensure that it's impossible to create a TypedTag instance whose 'tag' doesn't have type
+        // 'tag_type'.  Attempting to instantiate a tag with the wrong type will result in a compile
+        // error (no match for template specialization StaticAssert<false>), with no run-time cost.
+        static_assert(typeFromTag(tag) == tag_type, "mismatch between tag and tag_type");
+    }
+    operator Tag() const { return tag; }
+    int32_t maskedTag() { return static_cast<uint32_t>(tag) & 0x0FFFFFFF; }
+};
+
+template <Tag tag>
+struct Tag2TypedTag {
+    typedef TypedTag<typeFromTag(tag), tag> type;
+};
+
+#ifdef DECLARE_TYPED_TAG
+#undef DECLARE_TYPED_TAG
+#endif
+
+#define DECLARE_TYPED_TAG(name)                                    \
+    typedef typename Tag2TypedTag<Tag::name>::type TAG_##name##_t; \
+    static TAG_##name##_t TAG_##name;
+
+DECLARE_TYPED_TAG(ACTIVE_DATETIME);
+DECLARE_TYPED_TAG(ALGORITHM);
+DECLARE_TYPED_TAG(ALLOW_WHILE_ON_BODY);
+DECLARE_TYPED_TAG(APPLICATION_DATA);
+DECLARE_TYPED_TAG(APPLICATION_ID);
+DECLARE_TYPED_TAG(ASSOCIATED_DATA);
+DECLARE_TYPED_TAG(ATTESTATION_APPLICATION_ID);
+DECLARE_TYPED_TAG(ATTESTATION_CHALLENGE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_BRAND);
+DECLARE_TYPED_TAG(ATTESTATION_ID_DEVICE);
+DECLARE_TYPED_TAG(ATTESTATION_ID_IMEI);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MANUFACTURER);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MEID);
+DECLARE_TYPED_TAG(ATTESTATION_ID_PRODUCT);
+DECLARE_TYPED_TAG(ATTESTATION_ID_MODEL);
+DECLARE_TYPED_TAG(ATTESTATION_ID_SERIAL);
+DECLARE_TYPED_TAG(AUTH_TIMEOUT);
+DECLARE_TYPED_TAG(BLOCK_MODE);
+DECLARE_TYPED_TAG(BOOTLOADER_ONLY);
+DECLARE_TYPED_TAG(BOOT_PATCHLEVEL);
+DECLARE_TYPED_TAG(CALLER_NONCE);
+DECLARE_TYPED_TAG(CONFIRMATION_TOKEN);
+DECLARE_TYPED_TAG(CREATION_DATETIME);
+DECLARE_TYPED_TAG(DEVICE_UNIQUE_ATTESTATION);
+DECLARE_TYPED_TAG(DIGEST);
+DECLARE_TYPED_TAG(EARLY_BOOT_ONLY);
+DECLARE_TYPED_TAG(EC_CURVE);
+DECLARE_TYPED_TAG(HARDWARE_TYPE);
+DECLARE_TYPED_TAG(IDENTITY_CREDENTIAL_KEY);
+DECLARE_TYPED_TAG(INCLUDE_UNIQUE_ID);
+DECLARE_TYPED_TAG(INVALID);
+DECLARE_TYPED_TAG(KEY_SIZE);
+DECLARE_TYPED_TAG(MAC_LENGTH);
+DECLARE_TYPED_TAG(MAX_USES_PER_BOOT);
+DECLARE_TYPED_TAG(MIN_MAC_LENGTH);
+DECLARE_TYPED_TAG(MIN_SECONDS_BETWEEN_OPS);
+DECLARE_TYPED_TAG(NONCE);
+DECLARE_TYPED_TAG(NO_AUTH_REQUIRED);
+DECLARE_TYPED_TAG(ORIGIN);
+DECLARE_TYPED_TAG(ORIGINATION_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(OS_PATCHLEVEL);
+DECLARE_TYPED_TAG(OS_VERSION);
+DECLARE_TYPED_TAG(PADDING);
+DECLARE_TYPED_TAG(PURPOSE);
+DECLARE_TYPED_TAG(RESET_SINCE_ID_ROTATION);
+DECLARE_TYPED_TAG(ROLLBACK_RESISTANCE);
+DECLARE_TYPED_TAG(ROOT_OF_TRUST);
+DECLARE_TYPED_TAG(RSA_PUBLIC_EXPONENT);
+DECLARE_TYPED_TAG(STORAGE_KEY);
+DECLARE_TYPED_TAG(TRUSTED_CONFIRMATION_REQUIRED);
+DECLARE_TYPED_TAG(TRUSTED_USER_PRESENCE_REQUIRED);
+DECLARE_TYPED_TAG(UNIQUE_ID);
+DECLARE_TYPED_TAG(UNLOCKED_DEVICE_REQUIRED);
+DECLARE_TYPED_TAG(USAGE_COUNT_LIMIT);
+DECLARE_TYPED_TAG(USAGE_EXPIRE_DATETIME);
+DECLARE_TYPED_TAG(USER_AUTH_TYPE);
+DECLARE_TYPED_TAG(USER_ID);
+DECLARE_TYPED_TAG(USER_SECURE_ID);
+DECLARE_TYPED_TAG(VENDOR_PATCHLEVEL);
+DECLARE_TYPED_TAG(RSA_OAEP_MGF_DIGEST);
+DECLARE_TYPED_TAG(CERTIFICATE_SERIAL);
+DECLARE_TYPED_TAG(CERTIFICATE_SUBJECT);
+DECLARE_TYPED_TAG(CERTIFICATE_NOT_BEFORE);
+DECLARE_TYPED_TAG(CERTIFICATE_NOT_AFTER);
+DECLARE_TYPED_TAG(MAX_BOOT_LEVEL);
+
+#undef DECLARE_TYPED_TAG
+
+template <typename... Elems>
+struct MetaList {};
+
+using all_tags_t = MetaList<
+        TAG_INVALID_t, TAG_KEY_SIZE_t, TAG_MAC_LENGTH_t, TAG_CALLER_NONCE_t, TAG_MIN_MAC_LENGTH_t,
+        TAG_RSA_PUBLIC_EXPONENT_t, TAG_INCLUDE_UNIQUE_ID_t, TAG_ACTIVE_DATETIME_t,
+        TAG_ORIGINATION_EXPIRE_DATETIME_t, TAG_USAGE_EXPIRE_DATETIME_t,
+        TAG_MIN_SECONDS_BETWEEN_OPS_t, TAG_MAX_USES_PER_BOOT_t, TAG_USAGE_COUNT_LIMIT_t,
+        TAG_USER_ID_t, TAG_USER_SECURE_ID_t, TAG_NO_AUTH_REQUIRED_t, TAG_AUTH_TIMEOUT_t,
+        TAG_ALLOW_WHILE_ON_BODY_t, TAG_UNLOCKED_DEVICE_REQUIRED_t, TAG_APPLICATION_ID_t,
+        TAG_APPLICATION_DATA_t, TAG_CREATION_DATETIME_t, TAG_ROLLBACK_RESISTANCE_t,
+        TAG_HARDWARE_TYPE_t, TAG_ROOT_OF_TRUST_t, TAG_ASSOCIATED_DATA_t, TAG_NONCE_t,
+        TAG_BOOTLOADER_ONLY_t, TAG_OS_VERSION_t, TAG_OS_PATCHLEVEL_t, TAG_UNIQUE_ID_t,
+        TAG_ATTESTATION_CHALLENGE_t, TAG_ATTESTATION_APPLICATION_ID_t, TAG_ATTESTATION_ID_BRAND_t,
+        TAG_ATTESTATION_ID_DEVICE_t, TAG_ATTESTATION_ID_PRODUCT_t,
+        TAG_ATTESTATION_ID_MANUFACTURER_t, TAG_ATTESTATION_ID_MODEL_t,
+        TAG_RESET_SINCE_ID_ROTATION_t, TAG_PURPOSE_t, TAG_ALGORITHM_t, TAG_BLOCK_MODE_t,
+        TAG_DIGEST_t, TAG_PADDING_t, TAG_ORIGIN_t, TAG_USER_AUTH_TYPE_t, TAG_EC_CURVE_t,
+        TAG_BOOT_PATCHLEVEL_t, TAG_VENDOR_PATCHLEVEL_t, TAG_TRUSTED_CONFIRMATION_REQUIRED_t,
+        TAG_TRUSTED_USER_PRESENCE_REQUIRED_t, TAG_CERTIFICATE_SERIAL_t, TAG_CERTIFICATE_SUBJECT_t>;
+
+template <typename TypedTagType>
+struct TypedTag2ValueType;
+
+#ifdef MAKE_TAG_VALUE_ACCESSOR
+#undef MAKE_TAG_VALUE_ACCESSOR
+#endif
+
+#define MAKE_TAG_VALUE_ACCESSOR(tag_type, field_name)                                           \
+    template <Tag tag>                                                                          \
+    struct TypedTag2ValueType<TypedTag<tag_type, tag>> {                                        \
+        using type = std::remove_reference<                                                     \
+                decltype(static_cast<KeyParameterValue*>(nullptr)                               \
+                                 ->get<KeyParameterValue::field_name>())>::type;                \
+        static constexpr KeyParameterValue::Tag unionTag = KeyParameterValue::field_name;       \
+    };                                                                                          \
+    template <Tag tag>                                                                          \
+    inline std::optional<std::reference_wrapper<                                                \
+            const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>>                  \
+    accessTagValue(TypedTag<tag_type, tag>, const KeyParameter& param) {                        \
+        if (param.value.getTag() == KeyParameterValue::field_name) {                            \
+            return std::optional(                                                               \
+                    std::reference_wrapper(param.value.get<KeyParameterValue::field_name>()));  \
+        } else {                                                                                \
+            return std::nullopt;                                                                \
+        }                                                                                       \
+    }                                                                                           \
+    template <Tag tag>                                                                          \
+    inline std::optional<                                                                       \
+            std::reference_wrapper<typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>> \
+    accessTagValue(TypedTag<tag_type, tag>, KeyParameter& param) {                              \
+        if (param.value.getTag() == KeyParameterValue::field_name) {                            \
+            return std::optional(                                                               \
+                    std::reference_wrapper(param.value.get<KeyParameterValue::field_name>()));  \
+        } else {                                                                                \
+            return std::nullopt;                                                                \
+        }                                                                                       \
+    }
+
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::ULONG_REP, longInteger)
+MAKE_TAG_VALUE_ACCESSOR(TagType::DATE, dateTime)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::UINT_REP, integer)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BOOL, boolValue)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BYTES, blob)
+MAKE_TAG_VALUE_ACCESSOR(TagType::BIGNUM, blob)
+
+#undef MAKE_TAG_VALUE_ACCESSOR
+
+#ifdef MAKE_TAG_ENUM_VALUE_ACCESSOR
+#undef MAKE_TAG_ENUM_VALUE_ACCESSOR
+#endif
+
+#define MAKE_TAG_ENUM_VALUE_ACCESSOR(typed_tag, field_name)                                       \
+    template <>                                                                                   \
+    struct TypedTag2ValueType<decltype(typed_tag)> {                                              \
+        using type = std::remove_reference<                                                       \
+                decltype(static_cast<KeyParameterValue*>(nullptr)                                 \
+                                 ->get<KeyParameterValue::field_name>())>::type;                  \
+        static constexpr KeyParameterValue::Tag unionTag = KeyParameterValue::field_name;         \
+    };                                                                                            \
+    inline std::optional<                                                                         \
+            std::reference_wrapper<const typename TypedTag2ValueType<decltype(typed_tag)>::type>> \
+    accessTagValue(decltype(typed_tag), const KeyParameter& param) {                              \
+        if (param.value.getTag() == KeyParameterValue::field_name) {                              \
+            return std::optional(                                                                 \
+                    std::reference_wrapper(param.value.get<KeyParameterValue::field_name>()));    \
+        } else {                                                                                  \
+            return std::nullopt;                                                                  \
+        }                                                                                         \
+    }                                                                                             \
+    inline std::optional<                                                                         \
+            std::reference_wrapper<typename TypedTag2ValueType<decltype(typed_tag)>::type>>       \
+    accessTagValue(decltype(typed_tag), KeyParameter& param) {                                    \
+        if (param.value.getTag() == KeyParameterValue::field_name) {                              \
+            return std::optional(                                                                 \
+                    std::reference_wrapper(param.value.get<KeyParameterValue::field_name>()));    \
+        } else {                                                                                  \
+            return std::nullopt;                                                                  \
+        }                                                                                         \
+    }
+
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ALGORITHM, algorithm)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_BLOCK_MODE, blockMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_DIGEST, digest)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_EC_CURVE, ecCurve)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_ORIGIN, origin)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PADDING, paddingMode)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_PURPOSE, keyPurpose)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_USER_AUTH_TYPE, hardwareAuthenticatorType)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_HARDWARE_TYPE, securityLevel)
+MAKE_TAG_ENUM_VALUE_ACCESSOR(TAG_RSA_OAEP_MGF_DIGEST, digest)
+
+#undef MAKE_TAG_ENUM_VALUE_ACCESSOR
+
+template <TagType tag_type, Tag tag, typename ValueT>
+inline KeyParameter makeKeyParameter(TypedTag<tag_type, tag> ttag, ValueT&& value) {
+    KeyParameter retval;
+    retval.tag = tag;
+    retval.value = KeyParameterValue::make<TypedTag2ValueType<decltype(ttag)>::unionTag>(
+            std::forward<ValueT>(value));
+    return retval;
+}
+
+// the boolean case
+template <Tag tag>
+inline KeyParameter makeKeyParameter(TypedTag<TagType::BOOL, tag>) {
+    KeyParameter retval;
+    retval.tag = tag;
+    retval.value = KeyParameterValue::make<KeyParameterValue::boolValue>(true);
+    return retval;
+}
+
+// the invalid case
+inline KeyParameter makeKeyParameter(TypedTag<TagType::INVALID, Tag::INVALID>) {
+    KeyParameter retval;
+    retval.tag = Tag::INVALID;
+    retval.value = KeyParameterValue::make<KeyParameterValue::invalid>(0);
+    return retval;
+}
+
+template <typename... Pack>
+struct FirstOrNoneHelper;
+template <typename First>
+struct FirstOrNoneHelper<First> {
+    typedef First type;
+};
+template <>
+struct FirstOrNoneHelper<> {
+    struct type {};
+};
+
+template <typename... Pack>
+using FirstOrNone = typename FirstOrNoneHelper<Pack...>::type;
+
+template <TagType tag_type, Tag tag, typename... Args>
+inline KeyParameter Authorization(TypedTag<tag_type, tag> ttag, Args&&... args) {
+    static_assert(tag_type != TagType::BOOL || (sizeof...(args) == 0),
+                  "TagType::BOOL Authorizations do not take parameters. Presence is truth.");
+    static_assert(tag_type == TagType::BOOL || (sizeof...(args) == 1),
+                  "Authorization other then TagType::BOOL take exactly one parameter.");
+    static_assert(
+            tag_type == TagType::BOOL ||
+                    std::is_convertible<
+                            std::remove_cv_t<std::remove_reference_t<FirstOrNone<Args...>>>,
+                            typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>::value,
+            "Invalid argument type for given tag.");
+
+    return makeKeyParameter(ttag, std::forward<Args>(args)...);
+}
+
+template <typename T>
+std::remove_reference_t<T> NullOrOr(T&& v) {
+    if (v) return v;
+    return {};
+}
+
+template <typename Head, typename... Tail>
+std::remove_reference_t<Head> NullOrOr(Head&& head, Tail&&... tail) {
+    if (head) return head;
+    return NullOrOr(std::forward<Tail>(tail)...);
+}
+
+template <typename Default, typename Wrapped>
+std::remove_reference_t<Wrapped> defaultOr(std::optional<Wrapped>&& optional, Default&& def) {
+    static_assert(std::is_convertible<std::remove_reference_t<Default>,
+                                      std::remove_reference_t<Wrapped>>::value,
+                  "Type of default value must match the type wrapped by std::optional");
+    if (optional) return *optional;
+    return def;
+}
+
+template <TagType tag_type, Tag tag>
+inline std::optional<
+        std::reference_wrapper<const typename TypedTag2ValueType<TypedTag<tag_type, tag>>::type>>
+authorizationValue(TypedTag<tag_type, tag> ttag, const KeyParameter& param) {
+    // We only check if the parameter has the correct tag here; accessTagValue checks if the correct
+    // union field was initialized.
+    if (tag != param.tag) return {};
+    return accessTagValue(ttag, param);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/keymint_utils.h b/security/keymint/support/include/keymint_support/keymint_utils.h
new file mode 100644
index 0000000..e1ead21
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/keymint_utils.h
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/HardwareAuthToken.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using std::vector;
+
+inline static std::vector<uint8_t> blob2vector(const uint8_t* data, const size_t length) {
+    std::vector<uint8_t> result(data, data + length);
+    return result;
+}
+
+inline static std::vector<uint8_t> blob2vector(const std::string& value) {
+    vector<uint8_t> result(reinterpret_cast<const uint8_t*>(value.data()),
+                           reinterpret_cast<const uint8_t*>(value.data()) + value.size());
+    return result;
+}
+
+HardwareAuthToken vector2AuthToken(const vector<uint8_t>& buffer);
+vector<uint8_t> authToken2vector(const HardwareAuthToken& token);
+
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/keymint_support/openssl_utils.h b/security/keymint/support/include/keymint_support/openssl_utils.h
new file mode 100644
index 0000000..dee28ba
--- /dev/null
+++ b/security/keymint/support/include/keymint_support/openssl_utils.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/security/keymint/Digest.h>
+
+#include <openssl/evp.h>
+#include <openssl/x509.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+template <typename T, void (*F)(T*)>
+struct UniquePtrDeleter {
+    void operator()(T* p) const { F(p); }
+};
+
+typedef UniquePtrDeleter<EVP_PKEY, EVP_PKEY_free> EVP_PKEY_Delete;
+
+#define MAKE_OPENSSL_PTR_TYPE(type) \
+    typedef std::unique_ptr<type, UniquePtrDeleter<type, type##_free>> type##_Ptr;
+
+MAKE_OPENSSL_PTR_TYPE(ASN1_OBJECT)
+MAKE_OPENSSL_PTR_TYPE(BN_CTX)
+MAKE_OPENSSL_PTR_TYPE(EC_GROUP)
+MAKE_OPENSSL_PTR_TYPE(EC_KEY)
+MAKE_OPENSSL_PTR_TYPE(EC_POINT)
+MAKE_OPENSSL_PTR_TYPE(EVP_PKEY)
+MAKE_OPENSSL_PTR_TYPE(EVP_PKEY_CTX)
+MAKE_OPENSSL_PTR_TYPE(RSA)
+MAKE_OPENSSL_PTR_TYPE(X509)
+MAKE_OPENSSL_PTR_TYPE(X509_NAME)
+
+typedef std::unique_ptr<BIGNUM, UniquePtrDeleter<BIGNUM, BN_free>> BIGNUM_Ptr;
+
+inline const EVP_MD* openssl_digest(Digest digest) {
+    switch (digest) {
+        case Digest::NONE:
+            return nullptr;
+        case Digest::MD5:
+            return EVP_md5();
+        case Digest::SHA1:
+            return EVP_sha1();
+        case Digest::SHA_2_224:
+            return EVP_sha224();
+        case Digest::SHA_2_256:
+            return EVP_sha256();
+        case Digest::SHA_2_384:
+            return EVP_sha384();
+        case Digest::SHA_2_512:
+            return EVP_sha512();
+    }
+    return nullptr;
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/include/remote_prov/remote_prov_utils.h b/security/keymint/support/include/remote_prov/remote_prov_utils.h
new file mode 100644
index 0000000..406b7a9
--- /dev/null
+++ b/security/keymint/support/include/remote_prov/remote_prov_utils.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2019, 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.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include <keymaster/cppcose/cppcose.h>
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+
+using bytevec = std::vector<uint8_t>;
+using namespace cppcose;
+
+extern bytevec kTestMacKey;
+
+// The Google root key for the Endpoint Encryption Key chain, encoded as COSE_Sign1
+inline constexpr uint8_t kCoseEncodedRootCert[] = {
+        0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x2a, 0xa4, 0x01, 0x01, 0x03, 0x27, 0x20, 0x06,
+        0x21, 0x58, 0x20, 0x99, 0xb9, 0xee, 0xdd, 0x5e, 0xe4, 0x52, 0xf6, 0x85, 0xc6, 0x4c, 0x62,
+        0xdc, 0x3e, 0x61, 0xab, 0x57, 0x48, 0x7d, 0x75, 0x37, 0x29, 0xad, 0x76, 0x80, 0x32, 0xd2,
+        0xb3, 0xcb, 0x63, 0x58, 0xd9, 0x58, 0x40, 0x1e, 0x22, 0x08, 0x4b, 0xa4, 0xb7, 0xa4, 0xc8,
+        0xd7, 0x4e, 0x03, 0x0e, 0xfe, 0xb8, 0xaf, 0x14, 0x4c, 0xa7, 0x3b, 0x6f, 0xa5, 0xcd, 0xdc,
+        0xda, 0x79, 0xc6, 0x2b, 0x64, 0xfe, 0x99, 0x39, 0xaf, 0x76, 0xe7, 0x80, 0xfa, 0x66, 0x00,
+        0x85, 0x0d, 0x07, 0x98, 0x2a, 0xac, 0x91, 0x5c, 0xa7, 0x25, 0x14, 0x49, 0x06, 0x34, 0x75,
+        0xca, 0x8a, 0x27, 0x7a, 0xd9, 0xe3, 0x5a, 0x49, 0xeb, 0x02, 0x03};
+
+// The Google Endpoint Encryption Key certificate, encoded as COSE_Sign1
+inline constexpr uint8_t kCoseEncodedGeekCert[] = {
+        0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x58, 0x4e, 0xa5, 0x01, 0x01, 0x02, 0x58, 0x20,
+        0xd0, 0xae, 0xc1, 0x15, 0xca, 0x2a, 0xcf, 0x73, 0xae, 0x6b, 0xcc, 0xcb, 0xd1, 0x96,
+        0x1d, 0x65, 0xe8, 0xb1, 0xdd, 0xd7, 0x4a, 0x1a, 0x37, 0xb9, 0x43, 0x3a, 0x97, 0xd5,
+        0x99, 0xdf, 0x98, 0x08, 0x03, 0x38, 0x18, 0x20, 0x04, 0x21, 0x58, 0x20, 0xbe, 0x85,
+        0xe7, 0x46, 0xc4, 0xa3, 0x42, 0x5a, 0x40, 0xd9, 0x36, 0x3a, 0xa6, 0x15, 0xd0, 0x2c,
+        0x58, 0x7e, 0x3d, 0xdc, 0x33, 0x02, 0x32, 0xd2, 0xfc, 0x5e, 0x1e, 0x87, 0x25, 0x5f,
+        0x72, 0x60, 0x58, 0x40, 0x9b, 0xcf, 0x90, 0xe2, 0x2e, 0x4b, 0xab, 0xd1, 0x18, 0xb1,
+        0x0e, 0x8e, 0x5d, 0x20, 0x27, 0x4b, 0x84, 0x58, 0xfe, 0xfc, 0x32, 0x90, 0x7e, 0x72,
+        0x05, 0x83, 0xbc, 0xd7, 0x82, 0xbe, 0xfa, 0x64, 0x78, 0x2d, 0x54, 0x10, 0x4b, 0xc0,
+        0x31, 0xbf, 0x6b, 0xe8, 0x1e, 0x35, 0xe2, 0xf0, 0x2d, 0xce, 0x6c, 0x2f, 0x4f, 0xf2,
+        0xf5, 0x4f, 0xa5, 0xd4, 0x83, 0xad, 0x96, 0xa2, 0xf1, 0x87, 0x58, 0x04};
+
+/**
+ * Generates random bytes.
+ */
+bytevec randomBytes(size_t numBytes);
+
+struct EekChain {
+    bytevec chain;
+    bytevec last_pubkey;
+    bytevec last_privkey;
+};
+
+/**
+ * Generates an X25518 EEK with the specified eekId and an Ed25519 chain of the
+ * specified length. All keys are generated randomly.
+ */
+ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId);
+
+/**
+ * Returns the CBOR-encoded, production Google Endpoint Encryption Key chain.
+ */
+bytevec getProdEekChain();
+
+struct BccEntryData {
+    bytevec pubKey;
+};
+
+/**
+ * Validates the provided CBOR-encoded BCC, returning a vector of BccEntryData
+ * structs containing the BCC entry contents.  If an entry contains no firmware
+ * digest, the corresponding BccEntryData.firmwareDigest will have length zero
+ * (there's no way to distinguish between an empty and missing firmware digest,
+ * which seems fine).
+ */
+ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc);
+
+struct JsonOutput {
+    static JsonOutput Ok(std::string json) { return {std::move(json), ""}; }
+    static JsonOutput Error(std::string error) { return {"", std::move(error)}; }
+
+    std::string output;
+    std::string error;  // if non-empty, this describes what went wrong
+};
+
+/**
+ * Take a given certificate request and output a JSON blob containing both the
+ * build fingerprint and certificate request. This data may be serialized, then
+ * later uploaded to the remote provisioning service. The input csr is not
+ * validated, only encoded.
+ *
+ * Output format:
+ *   {
+ *     "build_fingerprint": <string>
+ *     "csr": <base64 CBOR CSR>
+ *   }
+ */
+JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr);
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/key_param_output.cpp b/security/keymint/support/key_param_output.cpp
new file mode 100644
index 0000000..0950eb6
--- /dev/null
+++ b/security/keymint/support/key_param_output.cpp
@@ -0,0 +1,42 @@
+/*
+ * 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 <keymint_support/key_param_output.h>
+
+#include <iomanip>
+
+#include <keymint_support/keymint_tags.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+using ::std::endl;
+using ::std::ostream;
+
+ostream& operator<<(ostream& os, const ::std::vector<KeyParameter>& set) {
+    if (set.size() == 0) {
+        os << "(Empty)" << endl;
+    } else {
+        os << "\n";
+        for (const auto& elem : set) os << elem << endl;
+    }
+    return os;
+}
+
+ostream& operator<<(ostream& os, const KeyParameter& param) {
+    return os << param.toString();
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/keymint_utils.cpp b/security/keymint/support/keymint_utils.cpp
new file mode 100644
index 0000000..1e0733f
--- /dev/null
+++ b/security/keymint/support/keymint_utils.cpp
@@ -0,0 +1,130 @@
+/*
+ * 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 <regex.h>
+
+#include <android-base/properties.h>
+#include <hardware/hw_auth_token.h>
+
+namespace aidl::android::hardware::security::keymint {
+
+namespace {
+
+constexpr char kPlatformVersionProp[] = "ro.build.version.release";
+constexpr char kPlatformVersionRegex[] = "^([0-9]{1,2})(\\.([0-9]{1,2}))?(\\.([0-9]{1,2}))?";
+constexpr size_t kMajorVersionMatch = 1;
+constexpr size_t kMinorVersionMatch = 3;
+constexpr size_t kSubminorVersionMatch = 5;
+constexpr size_t kPlatformVersionMatchCount = kSubminorVersionMatch + 1;
+
+constexpr char kPlatformPatchlevelProp[] = "ro.build.version.security_patch";
+constexpr char kVendorPatchlevelProp[] = "ro.vendor.build.security_patch";
+constexpr char kPatchlevelRegex[] = "^([0-9]{4})-([0-9]{2})-([0-9]{2})$";
+constexpr size_t kYearMatch = 1;
+constexpr size_t kMonthMatch = 2;
+constexpr size_t kDayMatch = 3;
+constexpr size_t kPatchlevelMatchCount = kDayMatch + 1;
+
+uint32_t match_to_uint32(const char* expression, const regmatch_t& match) {
+    if (match.rm_so == -1) return 0;
+
+    size_t len = match.rm_eo - match.rm_so;
+    std::string s(expression + match.rm_so, len);
+    return std::stoul(s);
+}
+
+std::string wait_and_get_property(const char* prop) {
+    std::string prop_value;
+    while (!::android::base::WaitForPropertyCreation(prop))
+        ;
+    prop_value = ::android::base::GetProperty(prop, "" /* default */);
+    return prop_value;
+}
+
+uint32_t getOsVersion(const char* version_str) {
+    regex_t regex;
+    if (regcomp(&regex, kPlatformVersionRegex, REG_EXTENDED)) {
+        return 0;
+    }
+
+    regmatch_t matches[kPlatformVersionMatchCount];
+    int not_match =
+            regexec(&regex, version_str, kPlatformVersionMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t major = match_to_uint32(version_str, matches[kMajorVersionMatch]);
+    uint32_t minor = match_to_uint32(version_str, matches[kMinorVersionMatch]);
+    uint32_t subminor = match_to_uint32(version_str, matches[kSubminorVersionMatch]);
+
+    return (major * 100 + minor) * 100 + subminor;
+}
+
+enum class PatchlevelOutput { kYearMonthDay, kYearMonth };
+
+uint32_t getPatchlevel(const char* patchlevel_str, PatchlevelOutput detail) {
+    regex_t regex;
+    if (regcomp(&regex, kPatchlevelRegex, REG_EXTENDED) != 0) {
+        return 0;
+    }
+
+    regmatch_t matches[kPatchlevelMatchCount];
+    int not_match = regexec(&regex, patchlevel_str, kPatchlevelMatchCount, matches, 0 /* flags */);
+    regfree(&regex);
+    if (not_match) {
+        return 0;
+    }
+
+    uint32_t year = match_to_uint32(patchlevel_str, matches[kYearMatch]);
+    uint32_t month = match_to_uint32(patchlevel_str, matches[kMonthMatch]);
+
+    if (month < 1 || month > 12) {
+        return 0;
+    }
+
+    switch (detail) {
+        case PatchlevelOutput::kYearMonthDay: {
+            uint32_t day = match_to_uint32(patchlevel_str, matches[kDayMatch]);
+            if (day < 1 || day > 31) {
+                return 0;
+            }
+            return year * 10000 + month * 100 + day;
+        }
+        case PatchlevelOutput::kYearMonth:
+            return year * 100 + month;
+    }
+}
+
+}  // anonymous namespace
+
+uint32_t getOsVersion() {
+    std::string version = wait_and_get_property(kPlatformVersionProp);
+    return getOsVersion(version.c_str());
+}
+
+uint32_t getOsPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kPlatformPatchlevelProp);
+    return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonth);
+}
+
+uint32_t getVendorPatchlevel() {
+    std::string patchlevel = wait_and_get_property(kVendorPatchlevelProp);
+    return getPatchlevel(patchlevel.c_str(), PatchlevelOutput::kYearMonthDay);
+}
+
+}  // namespace aidl::android::hardware::security::keymint
diff --git a/security/keymint/support/remote_prov_utils.cpp b/security/keymint/support/remote_prov_utils.cpp
new file mode 100644
index 0000000..0cbee51
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils.cpp
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2019, 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 <iterator>
+#include <tuple>
+
+#include <android-base/properties.h>
+#include <cppbor.h>
+#include <json/json.h>
+#include <openssl/base64.h>
+#include <openssl/rand.h>
+#include <remote_prov/remote_prov_utils.h>
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+
+bytevec kTestMacKey(32 /* count */, 0 /* byte value */);
+
+bytevec randomBytes(size_t numBytes) {
+    bytevec retval(numBytes);
+    RAND_bytes(retval.data(), numBytes);
+    return retval;
+}
+
+ErrMsgOr<EekChain> generateEekChain(size_t length, const bytevec& eekId) {
+    if (length < 2) {
+        return "EEK chain must contain at least 2 certs.";
+    }
+
+    auto eekChain = cppbor::Array();
+
+    bytevec prev_priv_key;
+    for (size_t i = 0; i < length - 1; ++i) {
+        bytevec pub_key(ED25519_PUBLIC_KEY_LEN);
+        bytevec priv_key(ED25519_PRIVATE_KEY_LEN);
+
+        ED25519_keypair(pub_key.data(), priv_key.data());
+
+        // The first signing key is self-signed.
+        if (prev_priv_key.empty()) prev_priv_key = priv_key;
+
+        auto coseSign1 = constructCoseSign1(prev_priv_key,
+                                            cppbor::Map() /* payload CoseKey */
+                                                    .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+                                                    .add(CoseKey::ALGORITHM, EDDSA)
+                                                    .add(CoseKey::CURVE, ED25519)
+                                                    .add(CoseKey::PUBKEY_X, pub_key)
+                                                    .canonicalize()
+                                                    .encode(),
+                                            {} /* AAD */);
+        if (!coseSign1) return coseSign1.moveMessage();
+        eekChain.add(coseSign1.moveValue());
+
+        prev_priv_key = priv_key;
+    }
+
+    bytevec pub_key(X25519_PUBLIC_VALUE_LEN);
+    bytevec priv_key(X25519_PRIVATE_KEY_LEN);
+    X25519_keypair(pub_key.data(), priv_key.data());
+
+    auto coseSign1 = constructCoseSign1(prev_priv_key,
+                                        cppbor::Map() /* payload CoseKey */
+                                                .add(CoseKey::KEY_TYPE, OCTET_KEY_PAIR)
+                                                .add(CoseKey::KEY_ID, eekId)
+                                                .add(CoseKey::ALGORITHM, ECDH_ES_HKDF_256)
+                                                .add(CoseKey::CURVE, cppcose::X25519)
+                                                .add(CoseKey::PUBKEY_X, pub_key)
+                                                .canonicalize()
+                                                .encode(),
+                                        {} /* AAD */);
+    if (!coseSign1) return coseSign1.moveMessage();
+    eekChain.add(coseSign1.moveValue());
+
+    return EekChain{eekChain.encode(), pub_key, priv_key};
+}
+
+bytevec getProdEekChain() {
+    bytevec prodEek;
+    prodEek.reserve(1 + sizeof(kCoseEncodedRootCert) + sizeof(kCoseEncodedGeekCert));
+
+    // In CBOR encoding, 0x82 indicates an array of two items
+    prodEek.push_back(0x82);
+    prodEek.insert(prodEek.end(), std::begin(kCoseEncodedRootCert), std::end(kCoseEncodedRootCert));
+    prodEek.insert(prodEek.end(), std::begin(kCoseEncodedGeekCert), std::end(kCoseEncodedGeekCert));
+
+    return prodEek;
+}
+
+ErrMsgOr<bytevec> verifyAndParseCoseSign1Cwt(const cppbor::Array* coseSign1,
+                                             const bytevec& signingCoseKey, const bytevec& aad) {
+    if (!coseSign1 || coseSign1->size() != kCoseSign1EntryCount) {
+        return "Invalid COSE_Sign1";
+    }
+
+    const cppbor::Bstr* protectedParams = coseSign1->get(kCoseSign1ProtectedParams)->asBstr();
+    const cppbor::Map* unprotectedParams = coseSign1->get(kCoseSign1UnprotectedParams)->asMap();
+    const cppbor::Bstr* payload = coseSign1->get(kCoseSign1Payload)->asBstr();
+    const cppbor::Bstr* signature = coseSign1->get(kCoseSign1Signature)->asBstr();
+
+    if (!protectedParams || !unprotectedParams || !payload || !signature) {
+        return "Invalid COSE_Sign1";
+    }
+
+    auto [parsedProtParams, _, errMsg] = cppbor::parse(protectedParams);
+    if (!parsedProtParams) {
+        return errMsg + " when parsing protected params.";
+    }
+    if (!parsedProtParams->asMap()) {
+        return "Protected params must be a map";
+    }
+
+    auto& algorithm = parsedProtParams->asMap()->get(ALGORITHM);
+    if (!algorithm || !algorithm->asInt() || algorithm->asInt()->value() != EDDSA) {
+        return "Unsupported signature algorithm";
+    }
+
+    // TODO(jbires): Handle CWTs as the CoseSign1 payload in a less hacky way. Since the CWT payload
+    //               is extremely remote provisioning specific, probably just make a separate
+    //               function there.
+    auto [parsedPayload, __, payloadErrMsg] = cppbor::parse(payload);
+    if (!parsedPayload) return payloadErrMsg + " when parsing key";
+    if (!parsedPayload->asMap()) return "CWT must be a map";
+    auto serializedKey = parsedPayload->asMap()->get(-4670552)->clone();
+    if (!serializedKey || !serializedKey->asBstr()) return "Could not find key entry";
+
+    bool selfSigned = signingCoseKey.empty();
+    auto key =
+            CoseKey::parseEd25519(selfSigned ? serializedKey->asBstr()->value() : signingCoseKey);
+    if (!key) return "Bad signing key: " + key.moveMessage();
+
+    bytevec signatureInput =
+            cppbor::Array().add("Signature1").add(*protectedParams).add(aad).add(*payload).encode();
+
+    if (!ED25519_verify(signatureInput.data(), signatureInput.size(), signature->value().data(),
+                        key->getBstrValue(CoseKey::PUBKEY_X)->data())) {
+        return "Signature verification failed";
+    }
+
+    return serializedKey->asBstr()->value();
+}
+
+ErrMsgOr<std::vector<BccEntryData>> validateBcc(const cppbor::Array* bcc) {
+    if (!bcc || bcc->size() == 0) return "Invalid BCC";
+
+    std::vector<BccEntryData> result;
+
+    bytevec prevKey;
+    // TODO(jbires): Actually process the pubKey at the start of the new bcc entry
+    for (size_t i = 1; i < bcc->size(); ++i) {
+        const cppbor::Array* entry = bcc->get(i)->asArray();
+        if (!entry || entry->size() != kCoseSign1EntryCount) {
+            return "Invalid BCC entry " + std::to_string(i) + ": " + prettyPrint(entry);
+        }
+        auto payload = verifyAndParseCoseSign1Cwt(entry, std::move(prevKey), bytevec{} /* AAD */);
+        if (!payload) {
+            return "Failed to verify entry " + std::to_string(i) + ": " + payload.moveMessage();
+        }
+
+        auto& certProtParms = entry->get(kCoseSign1ProtectedParams);
+        if (!certProtParms || !certProtParms->asBstr()) return "Invalid prot params";
+        auto [parsedProtParms, _, errMsg] = cppbor::parse(certProtParms->asBstr()->value());
+        if (!parsedProtParms || !parsedProtParms->asMap()) return "Invalid prot params";
+
+        result.push_back(BccEntryData{*payload});
+
+        // This entry's public key is the signing key for the next entry.
+        prevKey = payload.moveValue();
+    }
+
+    return result;
+}
+
+JsonOutput jsonEncodeCsrWithBuild(const cppbor::Array& csr) {
+    const std::string kFingerprintProp = "ro.build.fingerprint";
+
+    if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
+        return JsonOutput::Error("Unable to read build fingerprint");
+    }
+
+    bytevec csrCbor = csr.encode();
+    size_t base64Length;
+    int rc = EVP_EncodedLength(&base64Length, csrCbor.size());
+    if (!rc) {
+        return JsonOutput::Error("Error getting base64 length. Size overflow?");
+    }
+
+    std::vector<char> base64(base64Length);
+    rc = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(base64.data()), csrCbor.data(), csrCbor.size());
+    ++rc;  // Account for NUL, which BoringSSL does not for some reason.
+    if (rc != base64Length) {
+        return JsonOutput::Error("Error writing base64. Expected " + std::to_string(base64Length) +
+                                 " bytes to be written, but " + std::to_string(rc) +
+                                 " bytes were actually written.");
+    }
+
+    Json::Value json(Json::objectValue);
+    json["build_fingerprint"] = ::android::base::GetProperty(kFingerprintProp, /*default=*/"");
+    json["csr"] = base64.data();  // Boring writes a NUL-terminated c-string
+
+    Json::StreamWriterBuilder factory;
+    factory["indentation"] = "";  // disable pretty formatting
+    return JsonOutput::Ok(Json::writeString(factory, json));
+}
+
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/keymint/support/remote_prov_utils_test.cpp b/security/keymint/support/remote_prov_utils_test.cpp
new file mode 100644
index 0000000..8697c51
--- /dev/null
+++ b/security/keymint/support/remote_prov_utils_test.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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.
+ */
+
+#include <android-base/properties.h>
+#include <cppbor_parse.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/logger.h>
+#include <keymaster/remote_provisioning_utils.h>
+#include <openssl/curve25519.h>
+#include <remote_prov/remote_prov_utils.h>
+#include <cstdint>
+#include "cppbor.h"
+#include "keymaster/cppcose/cppcose.h"
+
+namespace aidl::android::hardware::security::keymint::remote_prov {
+namespace {
+
+using ::keymaster::KeymasterBlob;
+using ::keymaster::validateAndExtractEekPubAndId;
+using ::testing::ElementsAreArray;
+
+TEST(RemoteProvUtilsTest, GenerateEekChainInvalidLength) {
+    ASSERT_FALSE(generateEekChain(1, /*eekId=*/{}));
+}
+
+TEST(RemoteProvUtilsTest, GenerateEekChain) {
+    bytevec kTestEekId = {'t', 'e', 's', 't', 'I', 'd', 0};
+    for (size_t length : {2, 3, 31}) {
+        auto get_eek_result = generateEekChain(length, kTestEekId);
+        ASSERT_TRUE(get_eek_result) << get_eek_result.message();
+
+        auto& [chain, pubkey, privkey] = *get_eek_result;
+
+        auto validation_result = validateAndExtractEekPubAndId(
+                /*testMode=*/true, KeymasterBlob(chain.data(), chain.size()));
+        ASSERT_TRUE(validation_result.isOk());
+
+        auto& [eekPub, eekId] = *validation_result;
+        EXPECT_THAT(eekId, ElementsAreArray(kTestEekId));
+        EXPECT_THAT(eekPub, ElementsAreArray(pubkey));
+    }
+}
+
+TEST(RemoteProvUtilsTest, GetProdEekChain) {
+    auto chain = getProdEekChain();
+
+    auto validation_result = validateAndExtractEekPubAndId(
+            /*testMode=*/false, KeymasterBlob(chain.data(), chain.size()));
+    ASSERT_TRUE(validation_result.isOk()) << "Error: " << validation_result.moveError();
+
+    auto& [eekPub, eekId] = *validation_result;
+
+    auto [geekCert, ignoredNewPos, error] =
+            cppbor::parse(kCoseEncodedGeekCert, sizeof(kCoseEncodedGeekCert));
+    ASSERT_NE(geekCert, nullptr) << "Error: " << error;
+    ASSERT_NE(geekCert->asArray(), nullptr);
+
+    auto& encodedGeekCoseKey = geekCert->asArray()->get(kCoseSign1Payload);
+    ASSERT_NE(encodedGeekCoseKey, nullptr);
+    ASSERT_NE(encodedGeekCoseKey->asBstr(), nullptr);
+
+    auto geek = CoseKey::parse(encodedGeekCoseKey->asBstr()->value());
+    ASSERT_TRUE(geek) << "Error: " << geek.message();
+
+    const std::vector<uint8_t> empty;
+    EXPECT_THAT(eekId, ElementsAreArray(geek->getBstrValue(CoseKey::KEY_ID).value_or(empty)));
+    EXPECT_THAT(eekPub, ElementsAreArray(geek->getBstrValue(CoseKey::PUBKEY_X).value_or(empty)));
+}
+
+TEST(RemoteProvUtilsTest, JsonEncodeCsr) {
+    cppbor::Array array;
+    array.add(1);
+
+    auto [json, error] = jsonEncodeCsrWithBuild(array);
+
+    ASSERT_TRUE(error.empty()) << error;
+
+    std::string expected = R"({"build_fingerprint":")" +
+                           ::android::base::GetProperty("ro.build.fingerprint", /*default=*/"") +
+                           R"(","csr":"gQE="})";
+
+    ASSERT_EQ(json, expected);
+}
+
+}  // namespace
+}  // namespace aidl::android::hardware::security::keymint::remote_prov
diff --git a/security/secureclock/aidl/Android.bp b/security/secureclock/aidl/Android.bp
new file mode 100644
index 0000000..6a2b753
--- /dev/null
+++ b/security/secureclock/aidl/Android.bp
@@ -0,0 +1,35 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.secureclock",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/security/secureclock/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+            srcs_available: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+        rust: {
+            enabled: true,
+            apex_available: [
+                "//apex_available:platform",
+                "com.android.compos",
+            ],
+        },
+    },
+}
diff --git a/security/secureclock/aidl/OWNERS b/security/secureclock/aidl/OWNERS
new file mode 100644
index 0000000..a93b171
--- /dev/null
+++ b/security/secureclock/aidl/OWNERS
@@ -0,0 +1,4 @@
+jbires@google.com
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
new file mode 100644
index 0000000..4ecc1e4
--- /dev/null
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/ISecureClock.aidl
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.secureclock;
+/* @hide */
+@VintfStability
+interface ISecureClock {
+  android.hardware.security.secureclock.TimeStampToken generateTimeStamp(in long challenge);
+  const String TIME_STAMP_MAC_LABEL = "Auth Verification";
+}
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
new file mode 100644
index 0000000..d105ac8
--- /dev/null
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.secureclock;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable TimeStampToken {
+  long challenge;
+  android.hardware.security.secureclock.Timestamp timestamp;
+  byte[] mac;
+}
diff --git a/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
new file mode 100644
index 0000000..2e0e389
--- /dev/null
+++ b/security/secureclock/aidl/aidl_api/android.hardware.security.secureclock/current/android/hardware/security/secureclock/Timestamp.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.secureclock;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable Timestamp {
+  long milliSeconds;
+}
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
new file mode 100644
index 0000000..a742ff0
--- /dev/null
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/ISecureClock.aidl
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ * limitations under the License.
+ */
+
+package android.hardware.security.secureclock;
+import android.hardware.security.secureclock.TimeStampToken;
+
+/**
+ * Secure Clock definition.
+ *
+ * An ISecureClock provides a keymint service to generate secure timestamp using a secure platform.
+ * The secure time stamp contains time in milliseconds. This time stamp also contains a 256-bit MAC
+ * which provides integrity protection. The MAC is generated using HMAC-SHA-256 and a shared
+ * secret. The shared secret must be available to secure clock service by implementing
+ * ISharedSecret aidl. Note: ISecureClock depends on the shared secret, without which the secure
+ * time stamp token cannot be generated.
+ * @hide
+ */
+@VintfStability
+interface ISecureClock {
+    /**
+     * String used as context in the HMAC computation signing the generated time stamp.
+     * See TimeStampToken.mac for details.
+     */
+    const String TIME_STAMP_MAC_LABEL = "Auth Verification";
+
+    /**
+     * Generates an authenticated timestamp.
+     *
+     * @param A challenge value provided by the relying party. It will be included in the generated
+     *        TimeStampToken to ensure freshness. The relying service must ensure that the
+     *        challenge cannot be specified or predicted by an attacker.
+     *
+     * @return the TimeStampToken, see the definition for details.
+     */
+    TimeStampToken generateTimeStamp(in long challenge);
+}
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
new file mode 100644
index 0000000..fcf2ee8
--- /dev/null
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/TimeStampToken.aidl
@@ -0,0 +1,56 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.secureclock;
+
+import android.hardware.security.secureclock.Timestamp;
+
+/**
+ * TimeStampToken instances are used for secure environments that requires secure time information.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable TimeStampToken {
+    /**
+     * The challenge that was provided as argument to ISecureClock.generateTimeStamp by the client.
+     */
+    long challenge;
+
+    /**
+     * The current time of the secure environment that generates the TimeStampToken.
+     */
+    Timestamp timestamp;
+
+    /**
+     * 32-byte HMAC-SHA256 of the above values, computed as:
+     *
+     *    HMAC(H,
+     *         ISecureClock.TIME_STAMP_MAC_LABEL || challenge || timestamp || 1 )
+     *
+     * where:
+     *
+     *   ``ISecureClock.TIME_STAMP_MAC_LABEL'' is a string constant defined in ISecureClock.aidl.
+     *
+     *   ``H'' is the shared HMAC key (see computeSharedHmac() in ISharedSecret).
+     *
+     *   ``||'' represents concatenation
+     *
+     * The representation of challenge and timestamp is as 64-bit unsigned integers in big-endian
+     * order.  1, above, is a 32-bit unsigned integer, also big-endian.
+     */
+    byte[] mac;
+}
diff --git a/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
new file mode 100644
index 0000000..5061aa4
--- /dev/null
+++ b/security/secureclock/aidl/android/hardware/security/secureclock/Timestamp.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.security.secureclock;
+
+/**
+ * Time in milliseconds since some arbitrary point in time.  Time must be monotonically increasing,
+ * and a secure environment's notion of "current time" must not repeat until the Android device
+ * reboots, or until at least 50 million years have elapsed (note that this requirement is satisfied
+ * by setting the clock to zero during each boot, and then counting time accurately).
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable Timestamp {
+    long milliSeconds;
+}
diff --git a/security/secureclock/aidl/vts/functional/Android.bp b/security/secureclock/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..56c8e1d
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/Android.bp
@@ -0,0 +1,52 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsAidlSecureClockTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    srcs: [
+        "SecureClockAidlTest.cpp",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.secureclock-V1-ndk_platform",
+        "libkeymint",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
new file mode 100644
index 0000000..bf332d5
--- /dev/null
+++ b/security/secureclock/aidl/vts/functional/SecureClockAidlTest.cpp
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "secureclock_test"
+#include <android-base/logging.h>
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/secureclock/ISecureClock.h>
+#include <android/binder_manager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::secureclock::test {
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::std::shared_ptr;
+using ::std::string;
+using ::std::vector;
+
+class SecureClockAidlTest : public ::testing::TestWithParam<string> {
+  public:
+    struct TimestampTokenResult {
+        ErrorCode error;
+        TimeStampToken token;
+    };
+
+    TimestampTokenResult getTimestampToken(int64_t in_challenge) {
+        TimestampTokenResult result;
+        result.error =
+                GetReturnErrorCode(secureClock_->generateTimeStamp(in_challenge, &result.token));
+        return result;
+    }
+
+    uint64_t getTime() {
+        struct timespec timespec;
+        EXPECT_EQ(0, clock_gettime(CLOCK_BOOTTIME, &timespec));
+        return timespec.tv_sec * 1000 + timespec.tv_nsec / 1000000;
+    }
+
+    int sleep_ms(uint32_t milliseconds) {
+        struct timespec sleep_time = {static_cast<time_t>(milliseconds / 1000),
+                                      static_cast<long>(milliseconds % 1000) * 1000000};
+        while (sleep_time.tv_sec || sleep_time.tv_nsec) {
+            if (nanosleep(&sleep_time /* to wait */,
+                          &sleep_time /* remaining (on interrruption) */) == 0) {
+                sleep_time = {};
+            } else {
+                if (errno != EINTR) return errno;
+            }
+        }
+        return 0;
+    }
+
+    ErrorCode GetReturnErrorCode(const Status& result) {
+        if (result.isOk()) return ErrorCode::OK;
+
+        if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+            return static_cast<ErrorCode>(result.getServiceSpecificError());
+        }
+
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    void InitializeSecureClock(std::shared_ptr<ISecureClock> secureClock) {
+        ASSERT_NE(secureClock, nullptr);
+        secureClock_ = secureClock;
+    }
+
+    ISecureClock& secureClock() { return *secureClock_; }
+
+    static vector<string> build_params() {
+        auto params = ::android::getAidlHalInstanceNames(ISecureClock::descriptor);
+        return params;
+    }
+
+    void SetUp() override {
+        if (AServiceManager_isDeclared(GetParam().c_str())) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(GetParam().c_str()));
+            InitializeSecureClock(ISecureClock::fromBinder(binder));
+        } else {
+            InitializeSecureClock(nullptr);
+        }
+    }
+
+    void TearDown() override {}
+
+  private:
+    std::shared_ptr<ISecureClock> secureClock_;
+};
+
+/*
+ * The precise capabilities required to generate TimeStampToken will vary depending on the specific
+ * vendor implementations. The only thing we really can test is that tokens can be created by
+ * secureclock services, and that the timestamps increase as expected.
+ */
+TEST_P(SecureClockAidlTest, TestCreation) {
+    auto result1 = getTimestampToken(1 /* challenge */);
+    auto result1_time = getTime();
+    EXPECT_EQ(ErrorCode::OK, result1.error);
+    EXPECT_EQ(1U, result1.token.challenge);
+    EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+    EXPECT_EQ(32U, result1.token.mac.size());
+
+    unsigned long time_to_sleep = 200;
+    sleep_ms(time_to_sleep);
+
+    auto result2 = getTimestampToken(2 /* challenge */);
+    auto result2_time = getTime();
+    EXPECT_EQ(ErrorCode::OK, result2.error);
+    EXPECT_EQ(2U, result2.token.challenge);
+    EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
+    EXPECT_EQ(32U, result2.token.mac.size());
+
+    auto host_time_delta = result2_time - result1_time;
+
+    EXPECT_GE(host_time_delta, time_to_sleep)
+            << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+    EXPECT_LE(host_time_delta, time_to_sleep + 100)
+            << "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
+            << " ms?  That's awful!";
+    EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
+    unsigned long km_time_delta =
+            result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+    // 20 ms of slop just to avoid test flakiness.
+    EXPECT_LE(host_time_delta, km_time_delta + 20);
+    EXPECT_LE(km_time_delta, host_time_delta + 20);
+    ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+    ASSERT_NE(0,
+              memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+/*
+ * Test that the mac changes when the time stamp changes. This is does not guarantee that the time
+ * stamp is included in the mac but on failure we know that it is not. Other than in the test
+ * case above we call getTimestampToken with the exact same set of parameters.
+ */
+TEST_P(SecureClockAidlTest, MacChangesOnChangingTimestamp) {
+    auto result1 = getTimestampToken(0 /* challenge */);
+    auto result1_time = getTime();
+    EXPECT_EQ(ErrorCode::OK, result1.error);
+    EXPECT_EQ(0U, result1.token.challenge);
+    EXPECT_GT(result1.token.timestamp.milliSeconds, 0U);
+    EXPECT_EQ(32U, result1.token.mac.size());
+
+    unsigned long time_to_sleep = 200;
+    sleep_ms(time_to_sleep);
+
+    auto result2 = getTimestampToken(1 /* challenge */);
+    auto result2_time = getTime();
+    EXPECT_EQ(ErrorCode::OK, result2.error);
+    EXPECT_EQ(1U, result2.token.challenge);
+    EXPECT_GT(result2.token.timestamp.milliSeconds, 0U);
+    EXPECT_EQ(32U, result2.token.mac.size());
+
+    auto host_time_delta = result2_time - result1_time;
+
+    EXPECT_GE(host_time_delta, time_to_sleep)
+            << "We slept for " << time_to_sleep << " ms, the clock must have advanced by that much";
+    EXPECT_LE(host_time_delta, time_to_sleep + 100)
+            << "The getTimestampToken call took " << (host_time_delta - time_to_sleep)
+            << " ms?  That's awful!";
+
+    EXPECT_GE(result2.token.timestamp.milliSeconds, result1.token.timestamp.milliSeconds);
+    unsigned long km_time_delta =
+            result2.token.timestamp.milliSeconds - result1.token.timestamp.milliSeconds;
+
+    EXPECT_LE(host_time_delta, km_time_delta + 20);
+    EXPECT_LE(km_time_delta, host_time_delta + 20);
+    ASSERT_EQ(result1.token.mac.size(), result2.token.mac.size());
+    ASSERT_NE(0,
+              memcmp(result1.token.mac.data(), result2.token.mac.data(), result1.token.mac.size()));
+}
+
+INSTANTIATE_TEST_SUITE_P(PerInstance, SecureClockAidlTest,
+                         testing::ValuesIn(SecureClockAidlTest::build_params()),
+                         ::android::PrintInstanceNameToString);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SecureClockAidlTest);
+
+}  // namespace aidl::android::hardware::security::secureclock::test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/security/sharedsecret/aidl/Android.bp b/security/sharedsecret/aidl/Android.bp
new file mode 100644
index 0000000..1683059
--- /dev/null
+++ b/security/sharedsecret/aidl/Android.bp
@@ -0,0 +1,30 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.security.sharedsecret",
+    vendor_available: true,
+    srcs: [
+        "android/hardware/security/sharedsecret/*.aidl",
+    ],
+    stability: "vintf",
+    backend: {
+        java: {
+            sdk_version: "module_current",
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+        rust: {
+            enabled: true,
+        },
+    },
+}
diff --git a/security/sharedsecret/aidl/OWNERS b/security/sharedsecret/aidl/OWNERS
new file mode 100644
index 0000000..a93b171
--- /dev/null
+++ b/security/sharedsecret/aidl/OWNERS
@@ -0,0 +1,4 @@
+jbires@google.com
+jdanis@google.com
+seleneh@google.com
+swillden@google.com
diff --git a/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl
new file mode 100644
index 0000000..e76efe7
--- /dev/null
+++ b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/ISharedSecret.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ * limitations under the License.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.sharedsecret;
+/* @hide */
+@VintfStability
+interface ISharedSecret {
+  android.hardware.security.sharedsecret.SharedSecretParameters getSharedSecretParameters();
+  byte[] computeSharedSecret(in android.hardware.security.sharedsecret.SharedSecretParameters[] params);
+  const String KEY_AGREEMENT_LABEL = "KeymasterSharedMac";
+  const String KEY_CHECK_LABEL = "Keymaster HMAC Verification";
+}
diff --git a/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
new file mode 100644
index 0000000..e15fd49
--- /dev/null
+++ b/security/sharedsecret/aidl/aidl_api/android.hardware.security.sharedsecret/current/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.security.sharedsecret;
+/* @hide */
+@RustDerive(Clone=true, Eq=true, Hash=true, Ord=true, PartialEq=true, PartialOrd=true) @VintfStability
+parcelable SharedSecretParameters {
+  byte[] seed;
+  byte[] nonce;
+}
diff --git a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
new file mode 100644
index 0000000..eca8d87
--- /dev/null
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/ISharedSecret.aidl
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ * limitations under the License.
+ */
+
+package android.hardware.security.sharedsecret;
+import android.hardware.security.sharedsecret.SharedSecretParameters;
+
+/**
+ * Shared Secret definition.
+ *
+ * An ISharedSecret enables any service that implements this interface to establish a shared secret
+ * with one or more other services such as ISecureClock, TEE IKeymintDevice, StrongBox
+ * IKeymintDevice, etc. The shared secret is a 256-bit HMAC key and it is further used to generate
+ * secure tokens with integrity protection. There are three steps to establish a shared secret
+ * between the collaborating services:
+ *
+ * Step 1: During Android startup the system calls each service that implements this interface to
+ * get the shared secret parameters. This is done using getSharedSecretParameters method defined
+ * below.
+ * Step 2: The system lexicographically sorts the shared secret parameters received from each
+ * service and then sends these sorted parameter list to each service in a computeSharedSecret
+ * method defined below. The services individually computes the shared secret and returns back
+ * the 32 byte sharing check hash value generated by using the computed shared secret.
+ * Step 3: The system collects sharing check hash values from each service and evaluates them. If
+ * they are all equal, then the shared secret generation is considered to be successful else it is
+ * considered to have failed.
+ * @hide
+ */
+@VintfStability
+interface ISharedSecret {
+    /**
+     * String used as label in the shared key derivation. See computeSharedSecret below.
+     */
+    const String KEY_AGREEMENT_LABEL = "KeymasterSharedMac";
+
+    /**
+     * String used as context in the computation of the sharingCheck. See computeSharedSecret
+     * below.
+     */
+    const String KEY_CHECK_LABEL = "Keymaster HMAC Verification";
+
+    /**
+     * This method is the first step in the process for agreeing on a shared key.  It is called by
+     * Android during startup.  The system calls it on each of the HAL instances and collects the
+     * results in preparation for the second step.
+     *
+     * @return The SharedSecretParameters to use.  As specified in the SharedSecretParameters
+     *         documentation, the seed must contain the same value in every invocation
+     *         of the method on a given device, and the nonce must return the same value for every
+     *         invocation during a boot session.
+     */
+    SharedSecretParameters getSharedSecretParameters();
+
+    /**
+     * This method is the second and final step in the process for agreeing on a shared key.  It is
+     * called by Android during startup.  The system calls it on each of the HAL instances, and
+     * sends to it all of the SharedSecretParameters returned by all HAL instances.
+     *
+     * This method computes the shared 32-byte HMAC key ``H'' as follows (all HAL instances perform
+     * the same computation to arrive at the same result):
+     *
+     *     H = CKDF(key = K,
+     *              context = P1 || P2 || ... || Pn,
+     *              label = KEY_AGREEMENT_LABEL)
+     *
+     * where:
+     *
+     *     ``CKDF'' is the standard AES-CMAC KDF from NIST SP 800-108 in counter mode (see Section
+     *           5.1 of the referenced publication).  ``key'', ``context'', and ``label'' are
+     *           defined in the standard.  The counter is prefixed and length L appended, as shown
+     *           in the construction on page 12 of the standard.  The label string is UTF-8 encoded.
+     *
+     *     ``K'' is a pre-established shared secret, set up during factory reset.  The mechanism for
+     *           establishing this shared secret is implementation-defined.Any method of securely
+     *           establishing K that ensures that an attacker cannot obtain or derive its value is
+     *           acceptable.
+     *
+     *           CRITICAL SECURITY REQUIREMENT: All keys created by a IKeymintDevice instance must
+     *           be cryptographically bound to the value of K, such that establishing a new K
+     *           permanently destroys them.
+     *
+     *     ``||'' represents concatenation.
+     *
+     *     ``Pi'' is the i'th SharedSecretParameters value in the params vector. Encoding of an
+     *           SharedSecretParameters is the concatenation of its two fields, i.e. seed || nonce.
+     *
+     * Note that the label "KeymasterSharedMac" is the 18-byte UTF-8 encoding of the string.
+     *
+     * @param params is an array of SharedSecretParameters The lexicographically sorted
+     * SharedSecretParameters data returned by all HAL instances when getSharedSecretParameters
+     * was called.
+     *
+     * @return sharingCheck A 32-byte value used to verify that all the HAL instances have
+     *         computed the same shared HMAC key.  The sharingCheck value is computed as follows:
+     *
+     *             sharingCheck = HMAC(H, KEY_CHECK_LABEL)
+     *
+     *         The string is UTF-8 encoded, 27 bytes in length.  If the returned values of all
+     *         HAL instances don't match, clients must assume that HMAC agreement
+     *         failed.
+     */
+    byte[] computeSharedSecret(in SharedSecretParameters[] params);
+}
diff --git a/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
new file mode 100644
index 0000000..8144699
--- /dev/null
+++ b/security/sharedsecret/aidl/android/hardware/security/sharedsecret/SharedSecretParameters.aidl
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+package android.hardware.security.sharedsecret;
+
+/**
+ * SharedSecretParameters holds the data used in the process of establishing a shared secret i.e.
+ * HMAC key between multiple keymint services.  These parameters are returned in by
+ * getSharedSecretParameters() and send to computeShareSecret().  See the named methods in
+ * ISharedSecret for details of usage.
+ * @hide
+ */
+@VintfStability
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable SharedSecretParameters {
+    /**
+     * Either empty or contains a non zero persistent value that is associated with the pre-shared
+     * HMAC agreement key.  It is either empty or 32 bytes in length.
+     */
+    byte[] seed;
+
+    /**
+     * A 32-byte value which is guaranteed to be different each time
+     * getSharedSecretParameters() is called.  Probabilistic uniqueness (i.e. random) is acceptable,
+     * though a stronger uniqueness guarantee (e.g. counter) is recommended where possible.
+     */
+    byte[] nonce;
+}
diff --git a/security/sharedsecret/aidl/vts/functional/Android.bp b/security/sharedsecret/aidl/vts/functional/Android.bp
new file mode 100644
index 0000000..d3747fc
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/Android.bp
@@ -0,0 +1,52 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsAidlSharedSecretTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: [
+        "SharedSecretAidlTest.cpp",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+    ],
+    shared_libs: [
+        "libbinder_ndk",
+        "libcrypto",
+    ],
+    static_libs: [
+        "android.hardware.security.keymint-V1-ndk_platform",
+        "android.hardware.security.sharedsecret-V1-ndk_platform",
+        "libkeymint",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
new file mode 100644
index 0000000..51938ba
--- /dev/null
+++ b/security/sharedsecret/aidl/vts/functional/SharedSecretAidlTest.cpp
@@ -0,0 +1,376 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "sharedsecret_test"
+#include <android-base/logging.h>
+
+#include <aidl/Vintf.h>
+#include <aidl/android/hardware/security/keymint/ErrorCode.h>
+#include <aidl/android/hardware/security/sharedsecret/ISharedSecret.h>
+#include <android/binder_manager.h>
+#include <gtest/gtest.h>
+#include <vector>
+
+namespace aidl::android::hardware::security::sharedsecret::test {
+using ::aidl::android::hardware::security::keymint::ErrorCode;
+using ::std::shared_ptr;
+using ::std::vector;
+using Status = ::ndk::ScopedAStatus;
+
+class SharedSecretAidlTest : public ::testing::Test {
+  public:
+    struct GetParamsResult {
+        ErrorCode error;
+        SharedSecretParameters params;
+        auto tie() { return std::tie(error, params); }
+    };
+
+    struct ComputeResult {
+        ErrorCode error;
+        vector<uint8_t> sharing_check;
+        auto tie() { return std::tie(error, sharing_check); }
+    };
+
+    GetParamsResult getSharedSecretParameters(shared_ptr<ISharedSecret>& sharedSecret) {
+        SharedSecretParameters params;
+        auto error = GetReturnErrorCode(sharedSecret->getSharedSecretParameters(&params));
+        EXPECT_EQ(ErrorCode::OK, error);
+        EXPECT_TRUE(params.seed.size() == 0 || params.seed.size() == 32);
+        EXPECT_TRUE(params.nonce.size() == 32);
+
+        GetParamsResult result;
+        result.tie() = std::tie(error, params);
+        return result;
+    }
+
+    vector<SharedSecretParameters> getAllSharedSecretParameters() {
+        vector<SharedSecretParameters> paramsVec;
+        for (auto& sharedSecret : allSharedSecrets_) {
+            auto result = getSharedSecretParameters(sharedSecret);
+            EXPECT_EQ(ErrorCode::OK, result.error);
+            if (result.error == ErrorCode::OK) paramsVec.push_back(std::move(result.params));
+        }
+        return paramsVec;
+    }
+
+    ComputeResult computeSharedSecret(shared_ptr<ISharedSecret>& sharedSecret,
+                                      const vector<SharedSecretParameters>& params) {
+        std::vector<uint8_t> sharingCheck;
+        auto error = GetReturnErrorCode(sharedSecret->computeSharedSecret(params, &sharingCheck));
+        ComputeResult result;
+        result.tie() = std::tie(error, sharingCheck);
+        return result;
+    }
+
+    vector<ComputeResult> computeAllSharedSecrets(const vector<SharedSecretParameters>& params) {
+        vector<ComputeResult> result;
+        for (auto& sharedSecret : allSharedSecrets_) {
+            result.push_back(computeSharedSecret(sharedSecret, params));
+        }
+        return result;
+    }
+
+    vector<vector<uint8_t>> copyNonces(const vector<SharedSecretParameters>& paramsVec) {
+        vector<vector<uint8_t>> nonces;
+        for (auto& param : paramsVec) {
+            nonces.push_back(param.nonce);
+        }
+        return nonces;
+    }
+
+    void verifyResponses(const vector<uint8_t>& expected, const vector<ComputeResult>& responses) {
+        for (auto& response : responses) {
+            EXPECT_EQ(ErrorCode::OK, response.error);
+            EXPECT_EQ(expected, response.sharing_check) << "Sharing check values should match.";
+        }
+    }
+
+    ErrorCode GetReturnErrorCode(const Status& result) {
+        if (result.isOk()) return ErrorCode::OK;
+        if (result.getExceptionCode() == EX_SERVICE_SPECIFIC) {
+            return static_cast<ErrorCode>(result.getServiceSpecificError());
+        }
+        return ErrorCode::UNKNOWN_ERROR;
+    }
+
+    static shared_ptr<ISharedSecret> getSharedSecretService(const char* name) {
+        if (AServiceManager_isDeclared(name)) {
+            ::ndk::SpAIBinder binder(AServiceManager_waitForService(name));
+            return ISharedSecret::fromBinder(binder);
+        }
+        return nullptr;
+    }
+
+    const vector<shared_ptr<ISharedSecret>>& allSharedSecrets() { return allSharedSecrets_; }
+
+    static void SetUpTestCase() {
+        ASSERT_TRUE(allSharedSecrets_.empty()) << "The Shared Secret vector is not empty.";
+        auto names = ::android::getAidlHalInstanceNames(ISharedSecret::descriptor);
+        for (const auto& name : names) {
+            auto servicePtr = getSharedSecretService(name.c_str());
+            if (servicePtr != nullptr) allSharedSecrets_.push_back(std::move(servicePtr));
+        }
+    }
+
+    static void TearDownTestCase() {}
+    void SetUp() override {}
+    void TearDown() override {}
+
+  private:
+    static vector<shared_ptr<ISharedSecret>> allSharedSecrets_;
+};
+
+vector<shared_ptr<ISharedSecret>> SharedSecretAidlTest::allSharedSecrets_;
+
+TEST_F(SharedSecretAidlTest, GetParameters) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test because no shared secret service is found.";
+    }
+    for (auto sharedSecret : sharedSecrets) {
+        auto result1 = getSharedSecretParameters(sharedSecret);
+        EXPECT_EQ(ErrorCode::OK, result1.error);
+        auto result2 = getSharedSecretParameters(sharedSecret);
+        EXPECT_EQ(ErrorCode::OK, result2.error);
+        ASSERT_EQ(result1.params.seed, result2.params.seed)
+                << "A given shared secret service should always return the same seed.";
+        ASSERT_EQ(result1.params.nonce, result2.params.nonce)
+                << "A given shared secret service should always return the same nonce until "
+                   "restart.";
+    }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecret) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+    }
+    auto params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret services failed to provide parameters.";
+    auto nonces = copyNonces(params);
+    EXPECT_EQ(sharedSecrets.size(), nonces.size());
+    std::sort(nonces.begin(), nonces.end());
+    std::unique(nonces.begin(), nonces.end());
+    EXPECT_EQ(sharedSecrets.size(), nonces.size());
+
+    auto responses = computeAllSharedSecrets(params);
+    ASSERT_GT(responses.size(), 0U);
+    verifyResponses(responses[0].sharing_check, responses);
+
+    // Do it a second time.  Should get the same answers.
+    params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret services failed to provide parameters.";
+
+    responses = computeAllSharedSecrets(params);
+    ASSERT_GT(responses.size(), 0U);
+    ASSERT_EQ(32U, responses[0].sharing_check.size());
+    verifyResponses(responses[0].sharing_check, responses);
+}
+
+template <class F>
+class final_action {
+  public:
+    explicit final_action(F f) : f_(std::move(f)) {}
+    ~final_action() { f_(); }
+
+  private:
+    F f_;
+};
+
+template <class F>
+inline final_action<F> finally(const F& f) {
+    return final_action<F>(f);
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptNonce) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+    }
+    auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+
+    auto params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret services failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeAllSharedSecrets(params);
+
+    ASSERT_GT(responses.size(), 0U);
+    vector<uint8_t> correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param, a random byte within the param's nonce, and a random bit within
+    // the byte.  Flip that bit.
+    size_t param_to_tweak = rand() % params.size();
+    uint8_t byte_to_tweak = rand() % sizeof(params[param_to_tweak].nonce);
+    uint8_t bit_to_tweak = rand() % 8;
+    params[param_to_tweak].nonce[byte_to_tweak] ^= (1 << bit_to_tweak);
+
+    responses = computeAllSharedSecrets(params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                    << "Shared secret service that provided tweaked param should fail to compute "
+                       "shared secret";
+        } else {
+            EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+            EXPECT_NE(correct_response, responses[i].sharing_check)
+                    << "Others should calculate a different shared secret, due to the tweaked "
+                       "nonce.";
+        }
+    }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortNonce) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+    }
+    auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+
+    auto params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret services failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeAllSharedSecrets(params);
+
+    ASSERT_GT(responses.size(), 0U);
+    vector<uint8_t> correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param and shorten that nonce by one.
+    size_t param_to_tweak = rand() % params.size();
+    auto& to_tweak = params[param_to_tweak].nonce;
+    ASSERT_TRUE(to_tweak.size() == 32);
+    to_tweak.resize(31);
+
+    responses = computeAllSharedSecrets(params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                    << "Shared secret service that provided tweaked param should fail to compute "
+                       "shared secret";
+        } else {
+            // Other services *may* succeed, or may notice the invalid size for the nonce.
+            // However, if another service completes the computation, it should get the 'wrong'
+            // answer.
+            if (responses[i].error == ErrorCode::OK) {
+                EXPECT_NE(correct_response, responses[i].sharing_check)
+                        << "Others should calculate a different shared secret, due to the tweaked "
+                           "nonce.";
+            } else {
+                EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
+            }
+        }
+    }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretCorruptSeed) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+    }
+    auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+    auto params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret service failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeAllSharedSecrets(params);
+
+    ASSERT_GT(responses.size(), 0U);
+    vector<uint8_t> correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param and modify the seed.  We just increase the seed length by 1.  It doesn't
+    // matter what value is in the additional byte; it changes the seed regardless.
+    auto param_to_tweak = rand() % params.size();
+    auto& to_tweak = params[param_to_tweak].seed;
+    ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
+    if (!to_tweak.size()) {
+        to_tweak.resize(32);  // Contents don't matter; a little randomization is nice.
+    }
+    to_tweak[0]++;
+
+    responses = computeAllSharedSecrets(params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                    << "Shared secret service that provided tweaked param should fail to compute "
+                       "shared secret";
+        } else {
+            EXPECT_EQ(ErrorCode::OK, responses[i].error) << "Others should succeed";
+            EXPECT_NE(correct_response, responses[i].sharing_check)
+                    << "Others should calculate a different shared secret, due to the tweaked "
+                       "nonce.";
+        }
+    }
+}
+
+TEST_F(SharedSecretAidlTest, ComputeSharedSecretShortSeed) {
+    auto sharedSecrets = allSharedSecrets();
+    if (sharedSecrets.empty()) {
+        GTEST_SKIP() << "Skipping the test as no shared secret service is found.";
+    }
+    auto fixup_hmac = finally([&]() { computeAllSharedSecrets(getAllSharedSecretParameters()); });
+    auto params = getAllSharedSecretParameters();
+    ASSERT_EQ(sharedSecrets.size(), params.size())
+            << "One or more shared secret service failed to provide parameters.";
+
+    // All should be well in the normal case
+    auto responses = computeAllSharedSecrets(params);
+
+    ASSERT_GT(responses.size(), 0U);
+    vector<uint8_t> correct_response = responses[0].sharing_check;
+    verifyResponses(correct_response, responses);
+
+    // Pick a random param and modify the seed to be of (invalid) length 31.
+    auto param_to_tweak = rand() % params.size();
+    auto& to_tweak = params[param_to_tweak].seed;
+    ASSERT_TRUE(to_tweak.size() == 32 || to_tweak.size() == 0);
+    to_tweak.resize(31);
+
+    responses = computeAllSharedSecrets(params);
+    for (size_t i = 0; i < responses.size(); ++i) {
+        if (i == param_to_tweak) {
+            EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error)
+                    << "Shared secret service that provided tweaked param should fail to compute "
+                       "shared secret";
+        } else {
+            // Other services *may* succeed, or may notice the invalid size for the seed.
+            // However, if another service completes the computation, it should get the 'wrong'
+            // answer.
+            if (responses[i].error == ErrorCode::OK) {
+                EXPECT_NE(correct_response, responses[i].sharing_check)
+                        << "Others should calculate a different shared secret, due to the tweaked "
+                           "seed.";
+            } else {
+                EXPECT_EQ(ErrorCode::INVALID_ARGUMENT, responses[i].error);
+            }
+        }
+    }
+}
+
+}  // namespace aidl::android::hardware::security::sharedsecret::test
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    return RUN_ALL_TESTS();
+}
diff --git a/sensors/1.0/Android.bp b/sensors/1.0/Android.bp
index 509f72f..c81ee6d 100644
--- a/sensors/1.0/Android.bp
+++ b/sensors/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.sensors@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISensors.hal",
diff --git a/sensors/1.0/ISensors.hal b/sensors/1.0/ISensors.hal
index 8d41de2..0e172ef 100644
--- a/sensors/1.0/ISensors.hal
+++ b/sensors/1.0/ISensors.hal
@@ -103,7 +103,7 @@
      * Flush adds a FLUSH_COMPLETE metadata event to the end of the "batch mode"
      * FIFO for the specified sensor and flushes the FIFO.  If the FIFO is empty
      * or if the sensor doesn't support batching (FIFO size zero), return
-     * SUCCESS and add a trivial FLUSH_COMPLETE event added to the event stream.
+     * OK and add a trivial FLUSH_COMPLETE event added to the event stream.
      * This applies to all sensors other than one-shot sensors. If the sensor
      * is a one-shot sensor, flush must return BAD_VALUE and not generate any
      * flush complete metadata.  If the sensor is not active at the time flush()
diff --git a/sensors/1.0/default/Android.bp b/sensors/1.0/default/Android.bp
index d5c1b23..2e4e1b0 100644
--- a/sensors/1.0/default/Android.bp
+++ b/sensors/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.sensors@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/sensors/1.0/types.hal b/sensors/1.0/types.hal
index cbbe92f..ac7be06 100644
--- a/sensors/1.0/types.hal
+++ b/sensors/1.0/types.hal
@@ -1130,7 +1130,7 @@
 
     /**
      * High performance mode hint. Device is able to use up more power and take
-     * more reources to improve throughput and latency in high performance mode.
+     * more resources to improve throughput and latency in high performance mode.
      * One possible use case is virtual reality, when sensor latency need to be
      * carefully controlled.
      * int32_t: 1 or 0, denote if device is in/out of high performance mode,
diff --git a/sensors/1.0/vts/functional/Android.bp b/sensors/1.0/vts/functional/Android.bp
index c77733b..9a92fb3 100644
--- a/sensors/1.0/vts/functional/Android.bp
+++ b/sensors/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSensorsV1_0TargetTest",
     cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
diff --git a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
index e298651..1f579ba 100644
--- a/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
+++ b/sensors/1.0/vts/functional/VtsHalSensorsV1_0TargetTest.cpp
@@ -190,7 +190,7 @@
     });
 }
 
-// Test if sensor list returned is valid
+// Test if sensor hal can switch to different operation modes
 TEST_P(SensorsHidlTest, SetOperationMode) {
     std::vector<SensorInfo> sensorList = getSensorsList();
 
@@ -208,7 +208,7 @@
     ASSERT_EQ(Result::OK, S()->setOperationMode(OperationMode::NORMAL));
 }
 
-// Test if sensor list returned is valid
+// Test if sensor hal can receive injected events in loopback mode
 TEST_P(SensorsHidlTest, InjectSensorEventData) {
     std::vector<SensorInfo> sensorList = getSensorsList();
     std::vector<SensorInfo> sensorSupportInjection;
@@ -448,6 +448,7 @@
                               RateLevel::VERY_FAST, NullChecker<Event>());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SensorsHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SensorsHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISensors::descriptor)),
diff --git a/sensors/2.0/Android.bp b/sensors/2.0/Android.bp
index c8517c8..5a22b1f 100644
--- a/sensors/2.0/Android.bp
+++ b/sensors/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.sensors@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISensors.hal",
diff --git a/sensors/2.0/default/Android.bp b/sensors/2.0/default/Android.bp
index bb38327..04a490e 100644
--- a/sensors/2.0/default/Android.bp
+++ b/sensors/2.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.sensors@2.0-service.mock",
     defaults: ["hidl_defaults"],
diff --git a/sensors/2.0/default/SensorsV2_0.h b/sensors/2.0/default/SensorsV2_0.h
index 345835a..8270a35 100644
--- a/sensors/2.0/default/SensorsV2_0.h
+++ b/sensors/2.0/default/SensorsV2_0.h
@@ -28,6 +28,19 @@
 namespace implementation {
 
 struct SensorsV2_0 : public ::android::hardware::sensors::V2_X::implementation::Sensors<ISensors> {
+  Return<void>
+  getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+    std::vector<V1_0::SensorInfo> sensors;
+    for (const auto &sensor : mSensors) {
+      sensors.push_back(V2_1::implementation::convertToOldSensorInfo(
+          sensor.second->getSensorInfo()));
+    }
+
+    // Call the HIDL callback with the SensorInfo
+    _hidl_cb(sensors);
+
+    return Void();
+  }
 };
 
 }  // namespace implementation
diff --git a/sensors/2.0/multihal/Android.bp b/sensors/2.0/multihal/Android.bp
index bf51fcd..ae5c342 100644
--- a/sensors/2.0/multihal/Android.bp
+++ b/sensors/2.0/multihal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.sensors@2.0-service.multihal",
     defaults: [
diff --git a/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc
index 0b3d4c2..8867a1a 100644
--- a/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc
+++ b/sensors/2.0/multihal/android.hardware.sensors@2.0-service-multihal.rc
@@ -2,6 +2,6 @@
     class hal
     user system
     group system wakelock context_hub
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
     capabilities BLOCK_SUSPEND
     rlimit rtprio 10 10
diff --git a/sensors/2.0/vts/functional/Android.bp b/sensors/2.0/vts/functional/Android.bp
index 83ebc6b..cf7c9fa 100644
--- a/sensors/2.0/vts/functional/Android.bp
+++ b/sensors/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSensorsV2_0TargetTest",
     cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
diff --git a/sensors/2.1/Android.bp b/sensors/2.1/Android.bp
index 8e80e1f..5dd511a 100644
--- a/sensors/2.1/Android.bp
+++ b/sensors/2.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.sensors@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISensors.hal",
diff --git a/sensors/2.1/default/Android.bp b/sensors/2.1/default/Android.bp
index 27b439d..0be81e1 100644
--- a/sensors/2.1/default/Android.bp
+++ b/sensors/2.1/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.sensors@2.1-service.mock",
     defaults: ["hidl_defaults"],
diff --git a/sensors/2.1/default/SensorsV2_1.cpp b/sensors/2.1/default/SensorsV2_1.cpp
index 2e3d315..06446a2 100644
--- a/sensors/2.1/default/SensorsV2_1.cpp
+++ b/sensors/2.1/default/SensorsV2_1.cpp
@@ -45,7 +45,9 @@
         mSensorInfo.fifoReservedEventCount = 0;
         mSensorInfo.fifoMaxEventCount = 0;
         mSensorInfo.requiredPermission = "";
-        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE);
+        mSensorInfo.flags = static_cast<uint32_t>(V1_0::SensorFlagBits::ON_CHANGE_MODE |
+                                                  V1_0::SensorFlagBits::WAKE_UP |
+                                                  V1_0::SensorFlagBits::DATA_INJECTION);
     }
 };
 
diff --git a/sensors/2.1/default/SensorsV2_1.h b/sensors/2.1/default/SensorsV2_1.h
index 9f7fe04..c748383 100644
--- a/sensors/2.1/default/SensorsV2_1.h
+++ b/sensors/2.1/default/SensorsV2_1.h
@@ -54,6 +54,23 @@
     // Methods from ::android::hardware::sensors::V2_1::ISensors follow.
     Return<void> getSensorsList_2_1(ISensors::getSensorsList_2_1_cb _hidl_cb) override;
 
+    Return<void>
+    getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
+      std::vector<V1_0::SensorInfo> sensors;
+      for (const auto &sensor : mSensors) {
+        auto &info = sensor.second->getSensorInfo();
+        if (info.type != SensorType::HINGE_ANGLE) {
+          sensors.push_back(V2_1::implementation::convertToOldSensorInfo(
+              sensor.second->getSensorInfo()));
+        }
+      }
+
+      // Call the HIDL callback with the SensorInfo
+      _hidl_cb(sensors);
+
+      return Void();
+    }
+
     Return<Result> initialize_2_1(
             const ::android::hardware::MQDescriptorSync<V2_1::Event>& eventQueueDescriptor,
             const ::android::hardware::MQDescriptorSync<uint32_t>& wakeLockDescriptor,
@@ -71,4 +88,4 @@
 }  // namespace hardware
 }  // namespace android
 
-#endif  // ANDROID_HARDWARE_SENSORS_V2_1_H
\ No newline at end of file
+#endif // ANDROID_HARDWARE_SENSORS_V2_1_H
diff --git a/sensors/2.1/multihal/Android.bp b/sensors/2.1/multihal/Android.bp
index 6a7cac9..3846e19 100644
--- a/sensors/2.1/multihal/Android.bp
+++ b/sensors/2.1/multihal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.sensors@2.1-service.multihal",
     defaults: [
diff --git a/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
index fc99ee7..f47e060 100644
--- a/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
+++ b/sensors/2.1/multihal/android.hardware.sensors@2.1-service-multihal.rc
@@ -2,6 +2,6 @@
     class hal
     user system
     group system wakelock context_hub
-    writepid /dev/cpuset/system-background/tasks
+    task_profiles ServiceCapacityLow
     capabilities BLOCK_SUSPEND
     rlimit rtprio 10 10
diff --git a/sensors/2.1/vts/functional/Android.bp b/sensors/2.1/vts/functional/Android.bp
index d257993..3659e11 100644
--- a/sensors/2.1/vts/functional/Android.bp
+++ b/sensors/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSensorsV2_1TargetTest",
     cflags: [
diff --git a/sensors/common/default/2.X/Android.bp b/sensors/common/default/2.X/Android.bp
index 8b0d52f..82c942f 100644
--- a/sensors/common/default/2.X/Android.bp
+++ b/sensors/common/default/2.X/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "android.hardware.sensors@2.X-shared-impl",
     vendor: true,
diff --git a/sensors/common/default/2.X/Sensor.cpp b/sensors/common/default/2.X/Sensor.cpp
index 1841dff..1a7c628 100644
--- a/sensors/common/default/2.X/Sensor.cpp
+++ b/sensors/common/default/2.X/Sensor.cpp
@@ -26,6 +26,7 @@
 namespace V2_X {
 namespace implementation {
 
+using ::android::hardware::sensors::V1_0::EventPayload;
 using ::android::hardware::sensors::V1_0::MetaDataEventType;
 using ::android::hardware::sensors::V1_0::OperationMode;
 using ::android::hardware::sensors::V1_0::Result;
@@ -57,11 +58,11 @@
     return mSensorInfo;
 }
 
-void Sensor::batch(int32_t samplingPeriodNs) {
-    if (samplingPeriodNs < mSensorInfo.minDelay * 1000) {
-        samplingPeriodNs = mSensorInfo.minDelay * 1000;
-    } else if (samplingPeriodNs > mSensorInfo.maxDelay * 1000) {
-        samplingPeriodNs = mSensorInfo.maxDelay * 1000;
+void Sensor::batch(int64_t samplingPeriodNs) {
+    if (samplingPeriodNs < mSensorInfo.minDelay * 1000ll) {
+        samplingPeriodNs = mSensorInfo.minDelay * 1000ll;
+    } else if (samplingPeriodNs > mSensorInfo.maxDelay * 1000ll) {
+        samplingPeriodNs = mSensorInfo.maxDelay * 1000ll;
     }
 
     if (mSamplingPeriodNs != samplingPeriodNs) {
@@ -138,10 +139,8 @@
     event.sensorHandle = mSensorInfo.sensorHandle;
     event.sensorType = mSensorInfo.type;
     event.timestamp = ::android::elapsedRealtimeNano();
-    event.u.vec3.x = 0;
-    event.u.vec3.y = 0;
-    event.u.vec3.z = 0;
-    event.u.vec3.status = SensorStatus::ACCURACY_HIGH;
+    memset(&event.u, 0, sizeof(event.u));
+    readEventPayload(event.u);
     events.push_back(event);
     return events;
 }
@@ -189,7 +188,7 @@
 
     for (auto iter = events.begin(); iter != events.end(); ++iter) {
         Event ev = *iter;
-        if (ev.u.vec3 != mPreviousEvent.u.vec3 || !mPreviousEventSet) {
+        if (!mPreviousEventSet || memcmp(&mPreviousEvent.u, &ev.u, sizeof(ev.u)) != 0) {
             outputEvents.push_back(ev);
             mPreviousEvent = ev;
             mPreviousEventSet = true;
@@ -208,7 +207,7 @@
     mSensorInfo.maxRange = 78.4f;  // +/- 8g
     mSensorInfo.resolution = 1.52e-5;
     mSensorInfo.power = 0.001f;        // mA
-    mSensorInfo.minDelay = 20 * 1000;  // microseconds
+    mSensorInfo.minDelay = 10 * 1000;  // microseconds
     mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
@@ -216,6 +215,13 @@
     mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::DATA_INJECTION);
 };
 
+void AccelSensor::readEventPayload(EventPayload& payload) {
+    payload.vec3.x = 0;
+    payload.vec3.y = 0;
+    payload.vec3.z = -9.8;
+    payload.vec3.status = SensorStatus::ACCURACY_HIGH;
+}
+
 PressureSensor::PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
@@ -235,6 +241,10 @@
     mSensorInfo.flags = 0;
 };
 
+void PressureSensor::readEventPayload(EventPayload& payload) {
+    payload.scalar = 1013.25f;
+}
+
 MagnetometerSensor::MagnetometerSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
     : Sensor(callback) {
     mSensorInfo.sensorHandle = sensorHandle;
@@ -303,7 +313,7 @@
     mSensorInfo.maxRange = 1000.0f * M_PI / 180.0f;
     mSensorInfo.resolution = 1000.0f * M_PI / (180.0f * 32768.0f);
     mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 2.5f * 1000;  // microseconds
+    mSensorInfo.minDelay = 10 * 1000;  // microseconds
     mSensorInfo.maxDelay = kDefaultMaxDelayUs;
     mSensorInfo.fifoReservedEventCount = 0;
     mSensorInfo.fifoMaxEventCount = 0;
@@ -330,25 +340,6 @@
     mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::ON_CHANGE_MODE);
 };
 
-DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
-    : OnChangeSensor(callback) {
-    mSensorInfo.sensorHandle = sensorHandle;
-    mSensorInfo.name = "Device Temp Sensor";
-    mSensorInfo.vendor = "Vendor String";
-    mSensorInfo.version = 1;
-    mSensorInfo.type = SensorType::TEMPERATURE;
-    mSensorInfo.typeAsString = "";
-    mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 0.01f;
-    mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 40 * 1000;  // microseconds
-    mSensorInfo.maxDelay = kDefaultMaxDelayUs;
-    mSensorInfo.fifoReservedEventCount = 0;
-    mSensorInfo.fifoMaxEventCount = 0;
-    mSensorInfo.requiredPermission = "";
-    mSensorInfo.flags = static_cast<uint32_t>(SensorFlagBits::ON_CHANGE_MODE);
-}
-
 RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle,
                                                ISensorsEventCallback* callback)
     : OnChangeSensor(callback) {
diff --git a/sensors/common/default/2.X/Sensor.h b/sensors/common/default/2.X/Sensor.h
index 2f8a143..8ef11be 100644
--- a/sensors/common/default/2.X/Sensor.h
+++ b/sensors/common/default/2.X/Sensor.h
@@ -32,7 +32,7 @@
 namespace V2_X {
 namespace implementation {
 
-static constexpr float kDefaultMaxDelayUs = 10 * 1000 * 1000;
+static constexpr int32_t kDefaultMaxDelayUs = 10 * 1000 * 1000;
 
 class ISensorsEventCallback {
   public:
@@ -47,6 +47,7 @@
     using OperationMode = ::android::hardware::sensors::V1_0::OperationMode;
     using Result = ::android::hardware::sensors::V1_0::Result;
     using Event = ::android::hardware::sensors::V2_1::Event;
+    using EventPayload = ::android::hardware::sensors::V1_0::EventPayload;
     using SensorInfo = ::android::hardware::sensors::V2_1::SensorInfo;
     using SensorType = ::android::hardware::sensors::V2_1::SensorType;
 
@@ -54,7 +55,7 @@
     virtual ~Sensor();
 
     const SensorInfo& getSensorInfo() const;
-    void batch(int32_t samplingPeriodNs);
+    void batch(int64_t samplingPeriodNs);
     virtual void activate(bool enable);
     Result flush();
 
@@ -65,6 +66,7 @@
   protected:
     void run();
     virtual std::vector<Event> readEvents();
+    virtual void readEventPayload(EventPayload&) {}
     static void startThread(Sensor* sensor);
 
     bool isWakeUpSensor();
@@ -101,6 +103,9 @@
 class AccelSensor : public Sensor {
   public:
     AccelSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class GyroSensor : public Sensor {
@@ -113,14 +118,12 @@
     AmbientTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
 };
 
-class DeviceTempSensor : public OnChangeSensor {
-  public:
-    DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
-};
-
 class PressureSensor : public Sensor {
   public:
     PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
+
+  protected:
+    virtual void readEventPayload(EventPayload& payload) override;
 };
 
 class MagnetometerSensor : public Sensor {
diff --git a/sensors/common/default/2.X/Sensors.h b/sensors/common/default/2.X/Sensors.h
index ee8240d..1124425 100644
--- a/sensors/common/default/2.X/Sensors.h
+++ b/sensors/common/default/2.X/Sensors.h
@@ -64,7 +64,6 @@
         AddSensor<AccelSensor>();
         AddSensor<GyroSensor>();
         AddSensor<AmbientTempSensor>();
-        AddSensor<DeviceTempSensor>();
         AddSensor<PressureSensor>();
         AddSensor<MagnetometerSensor>();
         AddSensor<LightSensor>();
@@ -79,19 +78,6 @@
     }
 
     // Methods from ::android::hardware::sensors::V2_0::ISensors follow.
-    Return<void> getSensorsList(V2_0::ISensors::getSensorsList_cb _hidl_cb) override {
-        std::vector<V1_0::SensorInfo> sensors;
-        for (const auto& sensor : mSensors) {
-            sensors.push_back(
-                    V2_1::implementation::convertToOldSensorInfo(sensor.second->getSensorInfo()));
-        }
-
-        // Call the HIDL callback with the SensorInfo
-        _hidl_cb(sensors);
-
-        return Void();
-    }
-
     Return<Result> setOperationMode(OperationMode mode) override {
         for (auto sensor : mSensors) {
             sensor.second->setOperationMode(mode);
diff --git a/sensors/common/default/2.X/multihal/Android.bp b/sensors/common/default/2.X/multihal/Android.bp
index c80c47a..b0ad934 100644
--- a/sensors/common/default/2.X/multihal/Android.bp
+++ b/sensors/common/default/2.X/multihal/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.sensors@2.X-multihal-defaults",
     header_libs: [
diff --git a/sensors/common/default/2.X/multihal/HalProxy.cpp b/sensors/common/default/2.X/multihal/HalProxy.cpp
index fe3fc84..f5fc066 100644
--- a/sensors/common/default/2.X/multihal/HalProxy.cpp
+++ b/sensors/common/default/2.X/multihal/HalProxy.cpp
@@ -124,7 +124,9 @@
 Return<void> HalProxy::getSensorsList(ISensorsV2_0::getSensorsList_cb _hidl_cb) {
     std::vector<V1_0::SensorInfo> sensors;
     for (const auto& iter : mSensors) {
+      if (iter.second.type != SensorType::HINGE_ANGLE) {
         sensors.push_back(convertToOldSensorInfo(iter.second));
+      }
     }
     _hidl_cb(sensors);
     return Void();
@@ -341,7 +343,7 @@
         return Void();
     }
 
-    android::base::borrowed_fd writeFd = dup(fd->data[0]);
+    int writeFd = fd->data[0];
 
     std::ostringstream stream;
     stream << "===HalProxy===" << std::endl;
diff --git a/sensors/common/default/2.X/multihal/tests/Android.bp b/sensors/common/default/2.X/multihal/tests/Android.bp
index 1b60f4b..d8e7ce6 100644
--- a/sensors/common/default/2.X/multihal/tests/Android.bp
+++ b/sensors/common/default/2.X/multihal/tests/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "android.hardware.sensors@2.X-fakesubhal-defaults",
     srcs: [
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
index 1efd971..69debb6 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.cpp
@@ -71,9 +71,10 @@
     return mSensorInfo;
 }
 
-void Sensor::batch(int32_t samplingPeriodNs) {
-    samplingPeriodNs =
-            std::clamp(samplingPeriodNs, mSensorInfo.minDelay * 1000, mSensorInfo.maxDelay * 1000);
+void Sensor::batch(int64_t samplingPeriodNs) {
+    samplingPeriodNs = std::clamp(samplingPeriodNs,
+                                  static_cast<int64_t>(mSensorInfo.minDelay) * 1000,
+                                  static_cast<int64_t>(mSensorInfo.maxDelay) * 1000);
 
     if (mSamplingPeriodNs != samplingPeriodNs) {
         mSamplingPeriodNs = samplingPeriodNs;
@@ -323,17 +324,6 @@
     mSensorInfo.minDelay = 40 * 1000;  // microseconds
 }
 
-DeviceTempSensor::DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback)
-    : ContinuousSensor(sensorHandle, callback) {
-    mSensorInfo.name = "Device Temp Sensor";
-    mSensorInfo.type = SensorType::TEMPERATURE;
-    mSensorInfo.typeAsString = SENSOR_STRING_TYPE_TEMPERATURE;
-    mSensorInfo.maxRange = 80.0f;
-    mSensorInfo.resolution = 0.01f;
-    mSensorInfo.power = 0.001f;
-    mSensorInfo.minDelay = 40 * 1000;  // microseconds
-}
-
 RelativeHumiditySensor::RelativeHumiditySensor(int32_t sensorHandle,
                                                ISensorsEventCallback* callback)
     : OnChangeSensor(sensorHandle, callback) {
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
index 5cf9f83..08c8647 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/Sensor.h
@@ -49,7 +49,7 @@
     virtual ~Sensor();
 
     const SensorInfo& getSensorInfo() const;
-    void batch(int32_t samplingPeriodNs);
+    void batch(int64_t samplingPeriodNs);
     virtual void activate(bool enable);
     Result flush();
 
@@ -114,11 +114,6 @@
     std::vector<Event> readEvents() override;
 };
 
-class DeviceTempSensor : public ContinuousSensor {
-  public:
-    DeviceTempSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
-};
-
 class PressureSensor : public ContinuousSensor {
   public:
     PressureSensor(int32_t sensorHandle, ISensorsEventCallback* callback);
diff --git a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
index 1a78e84..353563c 100644
--- a/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
+++ b/sensors/common/default/2.X/multihal/tests/fake_subhal/SensorsSubHal.h
@@ -206,7 +206,6 @@
         ISensorsSubHalBase::AddSensor<GyroSensor>();
         ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
         ISensorsSubHalBase::AddSensor<PressureSensor>();
-        ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
     }
 };
 
@@ -231,7 +230,6 @@
         ISensorsSubHalBase::AddSensor<GyroSensor>();
         ISensorsSubHalBase::AddSensor<MagnetometerSensor>();
         ISensorsSubHalBase::AddSensor<PressureSensor>();
-        ISensorsSubHalBase::AddSensor<DeviceTempSensor>();
         ISensorsSubHalBase::AddSensor<AmbientTempSensor>();
         ISensorsSubHalBase::AddSensor<LightSensor>();
         ISensorsSubHalBase::AddSensor<ProximitySensor>();
diff --git a/sensors/common/utils/Android.bp b/sensors/common/utils/Android.bp
index aec6c4b..97e857c 100644
--- a/sensors/common/utils/Android.bp
+++ b/sensors/common/utils/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_headers {
     name: "android.hardware.sensors@2.X-shared-utils",
     vendor_available: true,
diff --git a/sensors/common/utils/EventMessageQueueWrapper.h b/sensors/common/utils/EventMessageQueueWrapper.h
index c4f92c8..63e4eb0 100644
--- a/sensors/common/utils/EventMessageQueueWrapper.h
+++ b/sensors/common/utils/EventMessageQueueWrapper.h
@@ -33,7 +33,7 @@
 namespace V2_1 {
 namespace implementation {
 
-class EventMessageQueueWrapperBase : public RefBase {
+class EventMessageQueueWrapperBase {
   public:
     virtual ~EventMessageQueueWrapperBase() {}
 
diff --git a/sensors/common/vts/2_X/Android.bp b/sensors/common/vts/2_X/Android.bp
index e5eceb5..4cf6a08 100644
--- a/sensors/common/vts/2_X/Android.bp
+++ b/sensors/common/vts/2_X/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "VtsHalSensorsV2_XTargetTest-defaults",
     cflags: ["-DLOG_TAG=\"sensors_hidl_hal_test\""],
diff --git a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
index 2e5aca4..47a8cc0 100644
--- a/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
+++ b/sensors/common/vts/2_X/VtsHalSensorsV2_XTargetTest.h
@@ -226,6 +226,7 @@
     void activateAllSensors(bool enable);
     std::vector<SensorInfoType> getNonOneShotSensors();
     std::vector<SensorInfoType> getNonOneShotAndNonSpecialSensors();
+    std::vector<SensorInfoType> getNonOneShotAndNonOnChangeAndNonSpecialSensors();
     std::vector<SensorInfoType> getOneShotSensors();
     std::vector<SensorInfoType> getInjectEventSensors();
     int32_t getInvalidSensorHandle();
@@ -326,6 +327,19 @@
     return sensors;
 }
 
+std::vector<SensorInfoType> SensorsHidlTest::getNonOneShotAndNonOnChangeAndNonSpecialSensors() {
+    std::vector<SensorInfoType> sensors;
+    for (const SensorInfoType& info : getSensorsList()) {
+        SensorFlagBits reportMode = extractReportMode(info.flags);
+        if (reportMode != SensorFlagBits::ONE_SHOT_MODE &&
+            reportMode != SensorFlagBits::ON_CHANGE_MODE &&
+            reportMode != SensorFlagBits::SPECIAL_REPORTING_MODE) {
+            sensors.push_back(info);
+        }
+    }
+    return sensors;
+}
+
 std::vector<SensorInfoType> SensorsHidlTest::getOneShotSensors() {
     std::vector<SensorInfoType> sensors;
     for (const SensorInfoType& info : getSensorsList()) {
@@ -352,7 +366,7 @@
     for (const SensorInfoType& sensor : getSensorsList()) {
         maxHandle = std::max(maxHandle, sensor.sensorHandle);
     }
-    return maxHandle + 1;
+    return maxHandle + 42;
 }
 
 // Test if sensor list returned is valid
@@ -448,6 +462,7 @@
 
     // Wait for events to be written back to the Event FMQ
     callback.waitForEvents(sensors, milliseconds(1000) /* timeout */);
+    getEnvironment()->unregisterCallback();
 
     for (const auto& s : sensors) {
         auto events = callback.getEvents(s.sensorHandle);
@@ -471,7 +486,6 @@
         ASSERT_EQ(lastEvent.u.vec3.status, injectedEvent.u.vec3.status);
     }
 
-    getEnvironment()->unregisterCallback();
     ASSERT_EQ(Result::OK, getSensors()->setOperationMode(OperationMode::NORMAL));
 }
 
@@ -589,7 +603,7 @@
                          << " type=" << static_cast<int>(sensor.type) << " name=" << sensor.name);
 
             Result flushResult = flush(sensor.sensorHandle);
-            ASSERT_EQ(flushResult, expectedResponse);
+            EXPECT_EQ(flushResult, expectedResponse);
         }
     }
 
@@ -726,8 +740,8 @@
     EventCallback callback;
     getEnvironment()->registerCallback(&callback);
 
-    // This test is not valid for one-shot or special-report-mode sensors
-    const std::vector<SensorInfoType> sensors = getNonOneShotAndNonSpecialSensors();
+    // This test is not valid for one-shot, on-change or special-report-mode sensors
+    const std::vector<SensorInfoType> sensors = getNonOneShotAndNonOnChangeAndNonSpecialSensors();
     milliseconds maxMinDelay(0);
     for (const SensorInfoType& sensor : sensors) {
         milliseconds minDelay = duration_cast<milliseconds>(microseconds(sensor.minDelay));
@@ -750,10 +764,7 @@
                      << " handle=0x" << std::hex << std::setw(8) << std::setfill('0')
                      << sensor.sensorHandle << std::dec << " type=" << static_cast<int>(sensor.type)
                      << " name=" << sensor.name);
-        // Some on-change sensors may not report an event without stimulus
-        if (extractReportMode(sensor.flags) != SensorFlagBits::ON_CHANGE_MODE) {
-            ASSERT_GE(callback.getEvents(sensor.sensorHandle).size(), 1);
-        }
+
         if (callback.getEvents(sensor.sensorHandle).size() >= 1) {
             lastEventTimestampMap[sensor.sensorHandle] =
                     callback.getEvents(sensor.sensorHandle).back().timestamp;
@@ -779,10 +790,7 @@
         if (lastEventTimestampMap.find(sensor.sensorHandle) == lastEventTimestampMap.end()) {
             continue;
         }
-        // Skip on-change sensors that do not consistently report an initial event
-        if (callback.getEvents(sensor.sensorHandle).size() < 1) {
-            continue;
-        }
+
         // Ensure that the first event received is not stale by ensuring that its timestamp is
         // sufficiently different from the previous event
         const EventType newEvent = callback.getEvents(sensor.sensorHandle).front();
@@ -837,7 +845,11 @@
         std::shared_ptr<SensorsTestSharedMemory<SensorTypeVersion, EventType>> mem,
         int32_t* directChannelHandle, bool supportsSharedMemType, bool supportsAnyDirectChannel) {
     char* buffer = mem->getBuffer();
-    memset(buffer, 0xff, mem->getSize());
+    size_t size = mem->getSize();
+
+    if (supportsSharedMemType) {
+        memset(buffer, 0xff, size);
+    }
 
     registerDirectChannel(mem->getSharedMemInfo(), [&](Result result, int32_t channelHandle) {
         if (supportsSharedMemType) {
diff --git a/sensors/common/vts/utils/Android.bp b/sensors/common/vts/utils/Android.bp
index baaed6c..44bed6e 100644
--- a/sensors/common/vts/utils/Android.bp
+++ b/sensors/common/vts/utils/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalSensorsTargetTestUtils",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
index 03bec87..a8e1996 100644
--- a/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
+++ b/sensors/common/vts/utils/include/sensors-vts-utils/SensorsHidlTestBase.h
@@ -109,7 +109,6 @@
         case SensorTypeT::MAGNETIC_FIELD:
         case SensorTypeT::ORIENTATION:
         case SensorTypeT::PRESSURE:
-        case SensorTypeT::TEMPERATURE:
         case SensorTypeT::GRAVITY:
         case SensorTypeT::LINEAR_ACCELERATION:
         case SensorTypeT::ROTATION_VECTOR:
@@ -145,6 +144,10 @@
         case SensorTypeT::DYNAMIC_SENSOR_META:
             return SensorFlagBits::SPECIAL_REPORTING_MODE;
 
+        case SensorTypeT::TEMPERATURE:
+            ALOGW("Device temperature sensor is deprecated, ignoring for test");
+            return (SensorFlagBits)-1;
+
         default:
             ALOGW("Type %d is not implemented in expectedReportModeForType", (int)type);
             return (SensorFlagBits)-1;
@@ -334,7 +337,7 @@
         usleep(500000);  // sleep 0.5 sec to wait for change rate to happen
         events1 = collectEvents(collectionTimeoutUs, minNEvent, getEnvironment());
 
-        // second collection, without stop sensor
+        // second collection, without stopping the sensor
         ASSERT_EQ(batch(handle, secondCollectionPeriod, batchingPeriodInNs), Result::OK);
 
         usleep(500000);  // sleep 0.5 sec to wait for change rate to happen
diff --git a/soundtrigger/2.0/Android.bp b/soundtrigger/2.0/Android.bp
index 07c05bc..996105c 100644
--- a/soundtrigger/2.0/Android.bp
+++ b/soundtrigger/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.soundtrigger@2.0",
     root: "android.hardware",
diff --git a/soundtrigger/2.0/default/Android.bp b/soundtrigger/2.0/default/Android.bp
index 1f9ae45..8236e30 100644
--- a/soundtrigger/2.0/default/Android.bp
+++ b/soundtrigger/2.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.soundtrigger@2.0-core",
     defaults: ["hidl_defaults"],
diff --git a/soundtrigger/2.0/default/Android.mk b/soundtrigger/2.0/default/Android.mk
index 835a020..17e4440 100644
--- a/soundtrigger/2.0/default/Android.mk
+++ b/soundtrigger/2.0/default/Android.mk
@@ -18,6 +18,9 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.soundtrigger@2.0-impl
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_VENDOR_MODULE := true
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SRC_FILES := \
diff --git a/soundtrigger/2.0/default/OWNERS b/soundtrigger/2.0/default/OWNERS
index 6fdc97c..ed739cf 100644
--- a/soundtrigger/2.0/default/OWNERS
+++ b/soundtrigger/2.0/default/OWNERS
@@ -1,3 +1,3 @@
 elaurent@google.com
-krocard@google.com
 mnaganov@google.com
+ytai@google.com
diff --git a/soundtrigger/2.0/vts/functional/Android.bp b/soundtrigger/2.0/vts/functional/Android.bp
index 86697bd..403fa9b 100644
--- a/soundtrigger/2.0/vts/functional/Android.bp
+++ b/soundtrigger/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSoundtriggerV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp b/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
index 63edec5..0065dbc 100644
--- a/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
+++ b/soundtrigger/2.0/vts/functional/VtsHalSoundtriggerV2_0TargetTest.cpp
@@ -293,6 +293,7 @@
     EXPECT_NE(0, hidlReturn);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoundTriggerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SoundTriggerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISoundTriggerHw::descriptor)),
diff --git a/soundtrigger/2.1/Android.bp b/soundtrigger/2.1/Android.bp
index 30173cb..7f6503c 100644
--- a/soundtrigger/2.1/Android.bp
+++ b/soundtrigger/2.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.soundtrigger@2.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ISoundTriggerHw.hal",
         "ISoundTriggerHwCallback.hal",
diff --git a/soundtrigger/2.1/ISoundTriggerHwCallback.hal b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
index 42e851b..b7720b6 100644
--- a/soundtrigger/2.1/ISoundTriggerHwCallback.hal
+++ b/soundtrigger/2.1/ISoundTriggerHwCallback.hal
@@ -22,7 +22,6 @@
 /**
  * SoundTrigger HAL Callback interface. Obtained during SoundTrigger setup.
  */
-@hidl_callback
 interface ISoundTriggerHwCallback extends @2.0::ISoundTriggerHwCallback {
 
     /**
diff --git a/soundtrigger/2.1/default/Android.mk b/soundtrigger/2.1/default/Android.mk
index b8d0407..602f5a7 100644
--- a/soundtrigger/2.1/default/Android.mk
+++ b/soundtrigger/2.1/default/Android.mk
@@ -18,6 +18,9 @@
 
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.soundtrigger@2.1-impl
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_VENDOR_MODULE := true
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_SRC_FILES := \
diff --git a/soundtrigger/2.1/vts/functional/Android.bp b/soundtrigger/2.1/vts/functional/Android.bp
index 9de913b..b013350 100644
--- a/soundtrigger/2.1/vts/functional/Android.bp
+++ b/soundtrigger/2.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSoundtriggerV2_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
index 392679d..91b8da8 100644
--- a/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
+++ b/soundtrigger/2.1/vts/functional/VtsHalSoundtriggerV2_1TargetTest.cpp
@@ -317,6 +317,7 @@
     EXPECT_NE(0, hidlReturn);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoundTriggerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SoundTriggerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISoundTriggerHw::descriptor)),
diff --git a/soundtrigger/2.2/Android.bp b/soundtrigger/2.2/Android.bp
index 7556aa4..f126eed 100644
--- a/soundtrigger/2.2/Android.bp
+++ b/soundtrigger/2.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.soundtrigger@2.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ISoundTriggerHw.hal",
     ],
diff --git a/soundtrigger/2.2/default/Android.bp b/soundtrigger/2.2/default/Android.bp
index db37c5b..768b9d6 100644
--- a/soundtrigger/2.2/default/Android.bp
+++ b/soundtrigger/2.2/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.soundtrigger@2.2-impl",
     relative_install_path: "hw",
diff --git a/soundtrigger/2.2/vts/functional/Android.bp b/soundtrigger/2.2/vts/functional/Android.bp
index b7967d9..faf6d58 100644
--- a/soundtrigger/2.2/vts/functional/Android.bp
+++ b/soundtrigger/2.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSoundtriggerV2_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/soundtrigger/2.2/vts/functional/VtsHalSoundtriggerV2_2TargetTest.cpp b/soundtrigger/2.2/vts/functional/VtsHalSoundtriggerV2_2TargetTest.cpp
index 1cce5a1..1352ca5 100644
--- a/soundtrigger/2.2/vts/functional/VtsHalSoundtriggerV2_2TargetTest.cpp
+++ b/soundtrigger/2.2/vts/functional/VtsHalSoundtriggerV2_2TargetTest.cpp
@@ -67,6 +67,7 @@
     EXPECT_TRUE(hidlReturn.isOk());
     EXPECT_EQ(-ENOSYS, hidlReturn);
 }
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SoundTriggerHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, SoundTriggerHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ISoundTriggerHw::descriptor)),
diff --git a/soundtrigger/2.3/Android.bp b/soundtrigger/2.3/Android.bp
index 3253a86..e65c0ad 100644
--- a/soundtrigger/2.3/Android.bp
+++ b/soundtrigger/2.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.soundtrigger@2.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISoundTriggerHw.hal",
diff --git a/soundtrigger/2.3/default/Android.bp b/soundtrigger/2.3/default/Android.bp
index be2c8b0..acb7ffe 100644
--- a/soundtrigger/2.3/default/Android.bp
+++ b/soundtrigger/2.3/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.soundtrigger@2.3-impl",
     relative_install_path: "hw",
diff --git a/soundtrigger/2.3/vts/functional/Android.bp b/soundtrigger/2.3/vts/functional/Android.bp
index 2c1b9e5..e613db5 100644
--- a/soundtrigger/2.3/vts/functional/Android.bp
+++ b/soundtrigger/2.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalSoundtriggerV2_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/tests/bar/1.0/Android.bp b/tests/bar/1.0/Android.bp
index 0aeccd6..ed05078 100644
--- a/tests/bar/1.0/Android.bp
+++ b/tests/bar/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.bar@1.0",
     root: "android.hardware",
diff --git a/tests/bar/1.0/default/Android.bp b/tests/bar/1.0/default/Android.bp
index 8e3d072..fd0cf0b 100644
--- a/tests/bar/1.0/default/Android.bp
+++ b/tests/bar/1.0/default/Android.bp
@@ -1,5 +1,14 @@
 
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.bar@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/baz/1.0/.hidl_for_test b/tests/baz/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/baz/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/baz/1.0/Android.bp b/tests/baz/1.0/Android.bp
index ed18876..0594f29 100644
--- a/tests/baz/1.0/Android.bp
+++ b/tests/baz/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.baz@1.0",
     root: "android.hardware",
diff --git a/tests/baz/1.0/IBase.hal b/tests/baz/1.0/IBase.hal
index 0c1b61f..7b60a8a 100644
--- a/tests/baz/1.0/IBase.hal
+++ b/tests/baz/1.0/IBase.hal
@@ -101,6 +101,8 @@
         DoubleThreeDim double3;
     };
 
+    isJava() generates (bool isJava);
+
     someBaseMethod();
 
     someBoolMethod(bool x) generates (bool y);
diff --git a/tests/baz/1.0/IBaz.hal b/tests/baz/1.0/IBaz.hal
index 7855446..8be1d61 100644
--- a/tests/baz/1.0/IBaz.hal
+++ b/tests/baz/1.0/IBaz.hal
@@ -94,7 +94,7 @@
         int8_t[7] array;
         string oneString;
         vec<string> vectorOfStrings;
-        IBase dummy;
+        interface iface;
     };
     oneway doThis(float param);
 
diff --git a/tests/baz/1.0/default/Android.bp b/tests/baz/1.0/default/Android.bp
index 4096d47..64ccb14 100644
--- a/tests/baz/1.0/default/Android.bp
+++ b/tests/baz/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.baz@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/baz/1.0/default/Baz.cpp b/tests/baz/1.0/default/Baz.cpp
index 2ce096c..0c3a59e 100644
--- a/tests/baz/1.0/default/Baz.cpp
+++ b/tests/baz/1.0/default/Baz.cpp
@@ -43,6 +43,12 @@
 }
 
 // Methods from ::android::hardware::tests::baz::V1_0::IBase follow.
+Return<bool> Baz::isJava() {
+    LOG(INFO) << "Baz::isJava";
+
+    return false;
+}
+
 Return<void> Baz::someBaseMethod() {
     LOG(INFO) << "Baz::someBaseMethod";
 
@@ -331,7 +337,7 @@
 Return<IBaz::SomeEnum> Baz::useAnEnum(IBaz::SomeEnum zzz) {
     LOG(INFO) << "useAnEnum " << (int)zzz;
 
-    return SomeEnum::goober;
+    return SomeEnum::quux;
 }
 
 Return<void> Baz::haveSomeStrings(const hidl_array<hidl_string, 3>& array,
diff --git a/tests/baz/1.0/default/Baz.h b/tests/baz/1.0/default/Baz.h
index 1e24d52..38bbcad 100644
--- a/tests/baz/1.0/default/Baz.h
+++ b/tests/baz/1.0/default/Baz.h
@@ -27,6 +27,7 @@
 
 struct Baz : public IBaz {
     // Methods from ::android::hardware::tests::baz::V1_0::IBase follow.
+    Return<bool> isJava() override;
     Return<void> someBaseMethod() override;
     Return<bool> someBoolMethod(bool x) override;
     Return<void> someBoolArrayMethod(const hidl_array<bool, 3>& x,
diff --git a/tests/expression/1.0/.hidl_for_test b/tests/expression/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/expression/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/expression/1.0/Android.bp b/tests/expression/1.0/Android.bp
index 4bc3848..8942e76 100644
--- a/tests/expression/1.0/Android.bp
+++ b/tests/expression/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.expression@1.0",
     root: "android.hardware",
diff --git a/tests/extension/light/2.0/.hidl_for_test b/tests/extension/light/2.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/extension/light/2.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/extension/light/2.0/Android.bp b/tests/extension/light/2.0/Android.bp
index e19a913..9e38ec9 100644
--- a/tests/extension/light/2.0/Android.bp
+++ b/tests/extension/light/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.extension.light@2.0",
     root: "android.hardware",
diff --git a/tests/extension/light/2.0/default/Android.bp b/tests/extension/light/2.0/default/Android.bp
index d8d8dd5..7a85da6 100644
--- a/tests/extension/light/2.0/default/Android.bp
+++ b/tests/extension/light/2.0/default/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.tests.extension.light@2.0-service",
     defaults: ["hidl_defaults"],
diff --git a/tests/extension/vibrator/aidl/Android.bp b/tests/extension/vibrator/aidl/Android.bp
index c64779c..75d1193 100644
--- a/tests/extension/vibrator/aidl/Android.bp
+++ b/tests/extension/vibrator/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     // This is an example test interface showing how to add functionality
     // with setExtension/getExtension
@@ -15,6 +24,9 @@
     // This is agreeing to keep the interface stable.
     stability: "vintf",
 
+    // This is a testing-purpose interface. Fine to use unstable version on REL platform.
+    owner: "test",
+
     // This happens to use types from a core interface, so we import it, but
     // this won't always be needed.
     imports: [
diff --git a/tests/extension/vibrator/aidl/client/Android.bp b/tests/extension/vibrator/aidl/client/Android.bp
index b0d8238..fb34d7e 100644
--- a/tests/extension/vibrator/aidl/client/Android.bp
+++ b/tests/extension/vibrator/aidl/client/Android.bp
@@ -2,6 +2,15 @@
 // context. All this code would look the same if it was running in system
 // server for example.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "android.hardware.tests.extension.vibrator-client",
     srcs: [
@@ -14,11 +23,11 @@
     shared_libs: [
         "libbinder",
         "libutils",
-        "android.hardware.vibrator-cpp",
-        "android.hardware.tests.extension.vibrator-cpp",
+        "android.hardware.vibrator-V1-cpp",
+        "android.hardware.tests.extension.vibrator-V1-cpp",
 
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
-        "android.hardware.tests.extension.vibrator-ndk_platform",
+        "android.hardware.vibrator-V1-ndk_platform",
+        "android.hardware.tests.extension.vibrator-V1-ndk_platform",
     ],
 }
diff --git a/tests/extension/vibrator/aidl/default/Android.bp b/tests/extension/vibrator/aidl/default/Android.bp
index ed40d25..17054f4 100644
--- a/tests/extension/vibrator/aidl/default/Android.bp
+++ b/tests/extension/vibrator/aidl/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.tests.extension.vibrator-service.example",
     relative_install_path: "hw",
@@ -19,7 +28,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
-        "android.hardware.tests.extension.vibrator-ndk_platform",
+        "android.hardware.vibrator-V1-ndk_platform",
+        "android.hardware.tests.extension.vibrator-V1-ndk_platform",
     ],
 }
diff --git a/tests/foo/1.0/.hidl_for_test b/tests/foo/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/foo/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/foo/1.0/Android.bp b/tests/foo/1.0/Android.bp
index 2f97fca..1c6ec58 100644
--- a/tests/foo/1.0/Android.bp
+++ b/tests/foo/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.foo@1.0",
     root: "android.hardware",
diff --git a/tests/foo/1.0/IFoo.hal b/tests/foo/1.0/IFoo.hal
index 4c54427..e242616 100644
--- a/tests/foo/1.0/IFoo.hal
+++ b/tests/foo/1.0/IFoo.hal
@@ -21,6 +21,7 @@
 import ISimple;
 import ITheirTypes.FloatArray;
 
+@SensitiveData // for test
 interface IFoo {
 
     enum SomeBaseEnum : uint8_t {
diff --git a/tests/foo/1.0/default/Android.bp b/tests/foo/1.0/default/Android.bp
index 48d6894..48f5b75 100644
--- a/tests/foo/1.0/default/Android.bp
+++ b/tests/foo/1.0/default/Android.bp
@@ -1,5 +1,14 @@
 
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.foo@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/foo/1.0/default/lib/Android.bp b/tests/foo/1.0/default/lib/Android.bp
index ba2081e..818e1c3 100644
--- a/tests/foo/1.0/default/lib/Android.bp
+++ b/tests/foo/1.0/default/lib/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "libfootest",
     defaults: ["hidl_defaults"],
diff --git a/tests/hash/1.0/.hidl_for_test b/tests/hash/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/hash/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/hash/1.0/Android.bp b/tests/hash/1.0/Android.bp
index 1095576..4d250bf 100644
--- a/tests/hash/1.0/Android.bp
+++ b/tests/hash/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.hash@1.0",
     root: "android.hardware",
diff --git a/tests/hash/1.0/default/Android.bp b/tests/hash/1.0/default/Android.bp
index 410b759..c1e0d9b 100644
--- a/tests/hash/1.0/default/Android.bp
+++ b/tests/hash/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.hash@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/inheritance/1.0/.hidl_for_test b/tests/inheritance/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/inheritance/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/inheritance/1.0/Android.bp b/tests/inheritance/1.0/Android.bp
index 0042b57..574f8dd 100644
--- a/tests/inheritance/1.0/Android.bp
+++ b/tests/inheritance/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.inheritance@1.0",
     root: "android.hardware",
diff --git a/tests/inheritance/1.0/default/Android.bp b/tests/inheritance/1.0/default/Android.bp
index 4a0c876..ff1cf53 100644
--- a/tests/inheritance/1.0/default/Android.bp
+++ b/tests/inheritance/1.0/default/Android.bp
@@ -1,5 +1,14 @@
 
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.inheritance@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/lazy/1.0/.hidl_for_test b/tests/lazy/1.0/.hidl_for_system_ext
similarity index 100%
rename from tests/lazy/1.0/.hidl_for_test
rename to tests/lazy/1.0/.hidl_for_system_ext
diff --git a/tests/lazy/1.0/Android.bp b/tests/lazy/1.0/Android.bp
index d2f8175..3ee307a 100644
--- a/tests/lazy/1.0/Android.bp
+++ b/tests/lazy/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.lazy@1.0",
     root: "android.hardware",
diff --git a/tests/lazy/1.0/.hidl_for_test b/tests/lazy/1.1/.hidl_for_system_ext
similarity index 100%
copy from tests/lazy/1.0/.hidl_for_test
copy to tests/lazy/1.1/.hidl_for_system_ext
diff --git a/tests/lazy/1.1/Android.bp b/tests/lazy/1.1/Android.bp
new file mode 100644
index 0000000..9429a23
--- /dev/null
+++ b/tests/lazy/1.1/Android.bp
@@ -0,0 +1,24 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.tests.lazy@1.1",
+    root: "android.hardware",
+    system_ext_specific: true,
+    srcs: [
+        "ILazy.hal",
+    ],
+    interfaces: [
+        "android.hardware.tests.lazy@1.0",
+        "android.hidl.base@1.0",
+    ],
+    gen_java: true,
+}
diff --git a/tests/lazy/1.1/ILazy.hal b/tests/lazy/1.1/ILazy.hal
new file mode 100644
index 0000000..eb48fd3
--- /dev/null
+++ b/tests/lazy/1.1/ILazy.hal
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tests.lazy@1.1;
+
+import android.hardware.tests.lazy@1.0;
+
+interface ILazy extends @1.0::ILazy {
+    /**
+     * Ask the process hosting the service to install a callback that notifies if there are
+     * services with clients.
+     * For testing purposes, this callback exercises the code to unregister/re-register
+     * the services and eventually shuts down the process.
+     */
+    setCustomActiveServicesCallback();
+};
diff --git a/tests/libhwbinder/1.0/.hidl_for_test b/tests/libhwbinder/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/libhwbinder/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/libhwbinder/1.0/Android.bp b/tests/libhwbinder/1.0/Android.bp
index 13af77c..8f45656 100644
--- a/tests/libhwbinder/1.0/Android.bp
+++ b/tests/libhwbinder/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.libhwbinder@1.0",
     root: "android.hardware",
diff --git a/tests/libhwbinder/1.0/default/Android.bp b/tests/libhwbinder/1.0/default/Android.bp
index 3bf08ed..1f80350 100644
--- a/tests/libhwbinder/1.0/default/Android.bp
+++ b/tests/libhwbinder/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.libhwbinder@1.0-impl.test",
     defaults: ["hidl_defaults"],
diff --git a/tests/libhwbinder/aidl/.hidl_for_test b/tests/libhwbinder/aidl/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/libhwbinder/aidl/.hidl_for_test
+++ /dev/null
diff --git a/tests/libhwbinder/aidl/Android.bp b/tests/libhwbinder/aidl/Android.bp
index c9e09f7..2b83f2b 100644
--- a/tests/libhwbinder/aidl/Android.bp
+++ b/tests/libhwbinder/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.libbinder",
     defaults: ["hidl_defaults"],
diff --git a/tests/memory/1.0/.hidl_for_test b/tests/memory/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/memory/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/memory/1.0/Android.bp b/tests/memory/1.0/Android.bp
index 6612e31..4bb724b 100644
--- a/tests/memory/1.0/Android.bp
+++ b/tests/memory/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.memory@1.0",
     root: "android.hardware",
diff --git a/tests/memory/1.0/default/Android.bp b/tests/memory/1.0/default/Android.bp
index 0293953..71d1b3d 100644
--- a/tests/memory/1.0/default/Android.bp
+++ b/tests/memory/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.memory@1.0-impl",
     defaults: ["hidl_defaults"],
@@ -32,4 +41,4 @@
     // These are static libs only for testing purposes and portability. Shared
     // libs should be used on device.
     static_libs: ["android.hardware.tests.memory@1.0"],
-}
\ No newline at end of file
+}
diff --git a/tests/memory/2.0/.hidl_for_test b/tests/memory/2.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/memory/2.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/memory/2.0/Android.bp b/tests/memory/2.0/Android.bp
index d24bd21..6eace8d 100644
--- a/tests/memory/2.0/Android.bp
+++ b/tests/memory/2.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.memory@2.0",
     root: "android.hardware",
diff --git a/tests/msgq/1.0/.hidl_for_test b/tests/msgq/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/msgq/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/msgq/1.0/Android.bp b/tests/msgq/1.0/Android.bp
index eea1ce6..8dd3870 100644
--- a/tests/msgq/1.0/Android.bp
+++ b/tests/msgq/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.msgq@1.0",
     root: "android.hardware",
diff --git a/tests/msgq/1.0/ITestMsgQ.hal b/tests/msgq/1.0/ITestMsgQ.hal
index 112270a..0cf9c7c 100644
--- a/tests/msgq/1.0/ITestMsgQ.hal
+++ b/tests/msgq/1.0/ITestMsgQ.hal
@@ -31,7 +31,7 @@
      *
      * @return ret True if the setup is successful.
      */
-    configureFmqSyncReadWrite(fmq_sync<uint16_t> mqDesc) generates(bool ret);
+    configureFmqSyncReadWrite(fmq_sync<int32_t> mqDesc) generates(bool ret);
 
     /**
      * This method requests the service to return an MQDescriptor to
@@ -41,12 +41,15 @@
      *
      * @param configureFmq The server sets up a new unsynchronized FMQ if
      * this parameter is true.
+     * @param userFd True to initialize the message queue with a user supplied
+     * file descriptor for the ring buffer.
+     * False to let the message queue use a single FD for everything.
      *
      * @return ret True if successful.
      * @return mqDesc This structure describes the unsynchronized FMQ that was
      * set up by the service. Client can use it to set up the FMQ at its end.
      */
-    getFmqUnsyncWrite(bool configureFmq) generates(bool ret, fmq_unsync<uint16_t> mqDesc);
+    getFmqUnsyncWrite(bool configureFmq, bool userFd) generates(bool ret, fmq_unsync<int32_t> mqDesc);
 
     /**
      * This method request the service to write into the synchronized read/write
diff --git a/tests/msgq/1.0/default/Android.bp b/tests/msgq/1.0/default/Android.bp
index e6408aa..5f116e7 100644
--- a/tests/msgq/1.0/default/Android.bp
+++ b/tests/msgq/1.0/default/Android.bp
@@ -13,13 +13,22 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.msgq@1.0-impl",
     defaults: ["hidl_defaults"],
     relative_install_path: "hw",
     srcs: [
         "TestMsgQ.cpp",
-        "BenchmarkMsgQ.cpp"
+        "BenchmarkMsgQ.cpp",
     ],
     shared_libs: [
         "libbase",
@@ -34,7 +43,7 @@
     // libs should be used on device.
     static_libs: [
         "android.hardware.tests.msgq@1.0",
-    ]
+    ],
 }
 
 cc_test {
@@ -49,7 +58,7 @@
         "libhidlbase",
         "liblog",
         "libutils",
-        "android.hardware.tests.msgq@1.0"
+        "android.hardware.tests.msgq@1.0",
     ],
     test_suites: ["general-tests"],
 }
@@ -67,14 +76,37 @@
         "libhidlbase",
         "liblog",
         "libutils",
+        "libbinder_ndk",
     ],
 
+    compile_multilib: "both",
+    multilib: {
+        lib32: {
+            suffix: "32",
+        },
+        lib64: {
+            suffix: "64",
+        },
+    },
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    auto_gen_config: false,
+
     // Allow dlsym'ing self for statically linked passthrough implementations
     ldflags: ["-rdynamic"],
 
     // These are static libs only for testing purposes and portability. Shared
     // libs should be used on device.
-    static_libs: ["android.hardware.tests.msgq@1.0"],
-    whole_static_libs: ["android.hardware.tests.msgq@1.0-impl"],
-    test_suites: ["general-tests"],
+    static_libs: [
+        "android.hardware.common-V2-ndk_platform",
+        "android.hardware.common.fmq-V1-ndk_platform",
+        "android.hardware.tests.msgq@1.0",
+        "android.fmq.test-ndk_platform",
+    ],
+    whole_static_libs: [
+        "android.hardware.tests.msgq@1.0-impl",
+        "android.fmq.test-impl",
+    ],
 }
diff --git a/tests/msgq/1.0/default/TestMsgQ.cpp b/tests/msgq/1.0/default/TestMsgQ.cpp
index ba665c9..4726ffe 100644
--- a/tests/msgq/1.0/default/TestMsgQ.cpp
+++ b/tests/msgq/1.0/default/TestMsgQ.cpp
@@ -25,7 +25,7 @@
 
 // Methods from ::android::hardware::tests::msgq::V1_0::ITestMsgQ follow.
 Return<bool> TestMsgQ::configureFmqSyncReadWrite(
-    const android::hardware::MQDescriptorSync<uint16_t>& mqDesc) {
+        const android::hardware::MQDescriptorSync<int32_t>& mqDesc) {
     mFmqSynchronized.reset(new (std::nothrow) MessageQueueSync(mqDesc));
     if ((mFmqSynchronized == nullptr) || (mFmqSynchronized->isValid() == false)) {
         return false;
@@ -41,10 +41,19 @@
     return true;
 }
 
-Return<void> TestMsgQ::getFmqUnsyncWrite(bool configureFmq, getFmqUnsyncWrite_cb _hidl_cb) {
+Return<void> TestMsgQ::getFmqUnsyncWrite(bool configureFmq, bool userFd,
+                                         getFmqUnsyncWrite_cb _hidl_cb) {
     if (configureFmq) {
         static constexpr size_t kNumElementsInQueue = 1024;
-        mFmqUnsynchronized.reset(new (std::nothrow) MessageQueueUnsync(kNumElementsInQueue));
+        static constexpr size_t kElementSizeBytes = sizeof(int32_t);
+        android::base::unique_fd ringbufferFd;
+        if (userFd) {
+            ringbufferFd.reset(
+                    ::ashmem_create_region("UnsyncWrite", kNumElementsInQueue * kElementSizeBytes));
+        }
+        mFmqUnsynchronized.reset(new (std::nothrow) MessageQueueUnsync(
+                kNumElementsInQueue, false, std::move(ringbufferFd),
+                kNumElementsInQueue * kElementSizeBytes));
     }
     if ((mFmqUnsynchronized == nullptr) ||
         (mFmqUnsynchronized->isValid() == false)) {
@@ -56,7 +65,7 @@
 }
 
 Return<bool> TestMsgQ::requestWriteFmqSync(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     for (int i = 0; i < count; i++) {
         data[i] = i;
     }
@@ -65,14 +74,14 @@
 }
 
 Return<bool> TestMsgQ::requestReadFmqSync(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     bool result = mFmqSynchronized->read(&data[0], count)
             && verifyData(&data[0], count);
     return result;
 }
 
 Return<bool> TestMsgQ::requestWriteFmqUnsync(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     for (int i = 0; i < count; i++) {
         data[i] = i;
     }
@@ -81,14 +90,14 @@
 }
 
 Return<bool> TestMsgQ::requestReadFmqUnsync(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     bool result =
             mFmqUnsynchronized->read(&data[0], count) && verifyData(&data[0], count);
     return result;
 }
 
 Return<void> TestMsgQ::requestBlockingRead(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     bool result = mFmqSynchronized->readBlocking(
             &data[0],
             count,
@@ -103,7 +112,7 @@
 }
 
 Return<void> TestMsgQ::requestBlockingReadDefaultEventFlagBits(int32_t count) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     bool result = mFmqSynchronized->readBlocking(
             &data[0],
             count);
@@ -116,7 +125,7 @@
 }
 
 Return<void> TestMsgQ::requestBlockingReadRepeat(int32_t count, int32_t numIter) {
-    std::vector<uint16_t> data(count);
+    std::vector<int32_t> data(count);
     for (int i = 0; i < numIter; i++) {
         bool result = mFmqSynchronized->readBlocking(
                 &data[0],
diff --git a/tests/msgq/1.0/default/TestMsgQ.h b/tests/msgq/1.0/default/TestMsgQ.h
index f9fcddd..49675fe 100644
--- a/tests/msgq/1.0/default/TestMsgQ.h
+++ b/tests/msgq/1.0/default/TestMsgQ.h
@@ -49,14 +49,15 @@
 using android::hardware::MessageQueue;
 
 struct TestMsgQ : public ITestMsgQ {
-    typedef MessageQueue<uint16_t, kSynchronizedReadWrite> MessageQueueSync;
-    typedef MessageQueue<uint16_t, kUnsynchronizedWrite> MessageQueueUnsync;
+    typedef MessageQueue<int32_t, kSynchronizedReadWrite> MessageQueueSync;
+    typedef MessageQueue<int32_t, kUnsynchronizedWrite> MessageQueueUnsync;
 
     TestMsgQ() : mFmqSynchronized(nullptr), mFmqUnsynchronized(nullptr) {}
 
     // Methods from ::android::hardware::tests::msgq::V1_0::ITestMsgQ follow.
-    Return<bool> configureFmqSyncReadWrite(const MQDescriptorSync<uint16_t>& mqDesc) override;
-    Return<void> getFmqUnsyncWrite(bool configureFmq, getFmqUnsyncWrite_cb _hidl_cb) override;
+    Return<bool> configureFmqSyncReadWrite(const MQDescriptorSync<int32_t>& mqDesc) override;
+    Return<void> getFmqUnsyncWrite(bool configureFmq, bool userFd,
+                                   getFmqUnsyncWrite_cb _hidl_cb) override;
     Return<bool> requestWriteFmqSync(int32_t count) override;
     Return<bool> requestReadFmqSync(int32_t count) override;
     Return<bool> requestWriteFmqUnsync(int32_t count) override;
@@ -73,7 +74,7 @@
     /*
      * Utility function to verify data read from the fast message queue.
      */
-    bool verifyData(uint16_t* data, int count) {
+    bool verifyData(int32_t* data, int count) {
         for (int i = 0; i < count; i++) {
             if (data[i] != i) return false;
         }
diff --git a/tests/msgq/1.0/default/mq_test_service.cpp b/tests/msgq/1.0/default/mq_test_service.cpp
index b5cb662..72ffe41 100644
--- a/tests/msgq/1.0/default/mq_test_service.cpp
+++ b/tests/msgq/1.0/default/mq_test_service.cpp
@@ -16,13 +16,32 @@
 
 #define LOG_TAG "FMQ_UnitTests"
 
+#include <TestAidlMsgQ.h>
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
 #include <android/hardware/tests/msgq/1.0/ITestMsgQ.h>
 
+using aidl::android::fmq::test::TestAidlMsgQ;
+
 #include <hidl/LegacySupport.h>
 
 using android::hardware::tests::msgq::V1_0::ITestMsgQ;
 using android::hardware::defaultPassthroughServiceImplementation;
 
 int main() {
-    return defaultPassthroughServiceImplementation<ITestMsgQ>();
+    android::hardware::details::setTrebleTestingOverride(true);
+    // Register AIDL service
+    ABinderProcess_startThreadPool();
+    std::shared_ptr<TestAidlMsgQ> store = ndk::SharedRefBase::make<TestAidlMsgQ>();
+
+    const std::string instance = std::string() + TestAidlMsgQ::descriptor + "/default";
+    LOG(INFO) << "instance: " << instance;
+    CHECK(AServiceManager_addService(store->asBinder().get(), instance.c_str()) == STATUS_OK);
+
+    // Register HIDL service
+    CHECK(defaultPassthroughServiceImplementation<ITestMsgQ>() == android::OK);
+    ABinderProcess_joinThreadPool();
+
+    return EXIT_FAILURE;  // should not reach
 }
diff --git a/tests/multithread/1.0/.hidl_for_test b/tests/multithread/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/multithread/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/multithread/1.0/Android.bp b/tests/multithread/1.0/Android.bp
index ed3a687..62bffb3 100644
--- a/tests/multithread/1.0/Android.bp
+++ b/tests/multithread/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.multithread@1.0",
     root: "android.hardware",
diff --git a/tests/multithread/1.0/default/Android.bp b/tests/multithread/1.0/default/Android.bp
index ff89938..80ab55e 100644
--- a/tests/multithread/1.0/default/Android.bp
+++ b/tests/multithread/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.multithread@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/safeunion/1.0/.hidl_for_test b/tests/safeunion/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/safeunion/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/safeunion/1.0/Android.bp b/tests/safeunion/1.0/Android.bp
index 2937832..ca1edfc 100644
--- a/tests/safeunion/1.0/Android.bp
+++ b/tests/safeunion/1.0/Android.bp
@@ -1,10 +1,18 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.safeunion@1.0",
     root: "android.hardware",
     srcs: [
-        "IOtherInterface.hal",
         "ISafeUnion.hal",
     ],
     interfaces: [
diff --git a/tests/safeunion/1.0/IOtherInterface.hal b/tests/safeunion/1.0/IOtherInterface.hal
deleted file mode 100644
index cdaf847..0000000
--- a/tests/safeunion/1.0/IOtherInterface.hal
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2018 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.
- */
-
-package android.hardware.tests.safeunion@1.0;
-
-interface IOtherInterface {
-    concatTwoStrings(string a, string b) generates (string result);
-};
diff --git a/tests/safeunion/1.0/ISafeUnion.hal b/tests/safeunion/1.0/ISafeUnion.hal
index 58c08c6..01e1727 100644
--- a/tests/safeunion/1.0/ISafeUnion.hal
+++ b/tests/safeunion/1.0/ISafeUnion.hal
@@ -16,8 +16,6 @@
 
 package android.hardware.tests.safeunion@1.0;
 
-import IOtherInterface;
-
 import android.hidl.safe_union@1.0::Monostate;
 
 interface ISafeUnion {
@@ -73,7 +71,7 @@
 
         uint32_t a;
         int8_t[7] b;
-        IOtherInterface c;
+        interface c;
         string d;
         vec<string> e;
         handle f;
@@ -107,7 +105,7 @@
     newInterfaceTypeSafeUnion() generates (InterfaceTypeSafeUnion myUnion);
     setInterfaceA(InterfaceTypeSafeUnion myUnion, uint32_t a) generates (InterfaceTypeSafeUnion myUnion);
     setInterfaceB(InterfaceTypeSafeUnion myUnion, int8_t[7] b) generates (InterfaceTypeSafeUnion myUnion);
-    setInterfaceC(InterfaceTypeSafeUnion myUnion, IOtherInterface c) generates (InterfaceTypeSafeUnion myUnion);
+    setInterfaceC(InterfaceTypeSafeUnion myUnion, interface c) generates (InterfaceTypeSafeUnion myUnion);
     setInterfaceD(InterfaceTypeSafeUnion myUnion, string d) generates (InterfaceTypeSafeUnion myUnion);
     setInterfaceE(InterfaceTypeSafeUnion myUnion, vec<string> e) generates (InterfaceTypeSafeUnion myUnion);
     setInterfaceF(InterfaceTypeSafeUnion myUnion, handle f) generates (InterfaceTypeSafeUnion myUnion);
diff --git a/tests/safeunion/1.0/default/Android.bp b/tests/safeunion/1.0/default/Android.bp
index 759a49c..94fee85 100644
--- a/tests/safeunion/1.0/default/Android.bp
+++ b/tests/safeunion/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.safeunion@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tests/safeunion/1.0/default/SafeUnion.cpp b/tests/safeunion/1.0/default/SafeUnion.cpp
index 4fb0974..667da6e 100644
--- a/tests/safeunion/1.0/default/SafeUnion.cpp
+++ b/tests/safeunion/1.0/default/SafeUnion.cpp
@@ -202,7 +202,9 @@
     return Void();
 }
 
-Return<void> SafeUnion::setInterfaceC(const InterfaceTypeSafeUnion& myUnion, const sp<::android::hardware::tests::safeunion::V1_0::IOtherInterface>& c, setInterfaceC_cb _hidl_cb) {
+Return<void> SafeUnion::setInterfaceC(const InterfaceTypeSafeUnion& myUnion,
+                                      const sp<::android::hidl::base::V1_0::IBase>& c,
+                                      setInterfaceC_cb _hidl_cb) {
     LOG(INFO) << "SERVER(SafeUnion) setInterfaceC(myUnion, " << toString(c) << ")";
 
     InterfaceTypeSafeUnion myNewUnion = myUnion;
diff --git a/tests/safeunion/1.0/default/SafeUnion.h b/tests/safeunion/1.0/default/SafeUnion.h
index ee3a954..b10575c 100644
--- a/tests/safeunion/1.0/default/SafeUnion.h
+++ b/tests/safeunion/1.0/default/SafeUnion.h
@@ -54,7 +54,9 @@
     Return<void> newInterfaceTypeSafeUnion(newInterfaceTypeSafeUnion_cb _hidl_cb) override;
     Return<void> setInterfaceA(const InterfaceTypeSafeUnion& myUnion, uint32_t a, setInterfaceA_cb _hidl_cb) override;
     Return<void> setInterfaceB(const InterfaceTypeSafeUnion& myUnion, const hidl_array<int8_t, 7>& b, setInterfaceB_cb _hidl_cb) override;
-    Return<void> setInterfaceC(const InterfaceTypeSafeUnion& myUnion, const sp<::android::hardware::tests::safeunion::V1_0::IOtherInterface>& c, setInterfaceC_cb _hidl_cb) override;
+    Return<void> setInterfaceC(const InterfaceTypeSafeUnion& myUnion,
+                               const sp<::android::hidl::base::V1_0::IBase>& c,
+                               setInterfaceC_cb _hidl_cb) override;
     Return<void> setInterfaceD(const InterfaceTypeSafeUnion& myUnion, const hidl_string& d, setInterfaceD_cb _hidl_cb) override;
     Return<void> setInterfaceE(const InterfaceTypeSafeUnion& myUnion, const hidl_vec<hidl_string>& e, setInterfaceE_cb _hidl_cb) override;
     Return<void> setInterfaceF(const InterfaceTypeSafeUnion& myUnion, const hidl_handle& f,
diff --git a/tests/safeunion/cpp/1.0/.hidl_for_test b/tests/safeunion/cpp/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/safeunion/cpp/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/safeunion/cpp/1.0/Android.bp b/tests/safeunion/cpp/1.0/Android.bp
index 221643a..6c670f6 100644
--- a/tests/safeunion/cpp/1.0/Android.bp
+++ b/tests/safeunion/cpp/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.safeunion.cpp@1.0",
     root: "android.hardware",
diff --git a/tests/safeunion/cpp/1.0/default/Android.bp b/tests/safeunion/cpp/1.0/default/Android.bp
index 618f295..25f3750 100644
--- a/tests/safeunion/cpp/1.0/default/Android.bp
+++ b/tests/safeunion/cpp/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.safeunion.cpp@1.0-impl",
     relative_install_path: "hw",
diff --git a/tests/trie/1.0/.hidl_for_test b/tests/trie/1.0/.hidl_for_test
deleted file mode 100644
index e69de29..0000000
--- a/tests/trie/1.0/.hidl_for_test
+++ /dev/null
diff --git a/tests/trie/1.0/Android.bp b/tests/trie/1.0/Android.bp
index 3cb67c7..e702540 100644
--- a/tests/trie/1.0/Android.bp
+++ b/tests/trie/1.0/Android.bp
@@ -1,5 +1,14 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tests.trie@1.0",
     root: "android.hardware",
diff --git a/tests/trie/1.0/default/Android.bp b/tests/trie/1.0/default/Android.bp
index 4ca705c..3690802 100644
--- a/tests/trie/1.0/default/Android.bp
+++ b/tests/trie/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library {
     name: "android.hardware.tests.trie@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tetheroffload/config/1.0/Android.bp b/tetheroffload/config/1.0/Android.bp
index 321224a..116c9b6 100644
--- a/tetheroffload/config/1.0/Android.bp
+++ b/tetheroffload/config/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tetheroffload.config@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IOffloadConfig.hal",
     ],
diff --git a/tetheroffload/config/1.0/vts/functional/Android.bp b/tetheroffload/config/1.0/vts/functional/Android.bp
index ad5a1b1..fe03d8f 100644
--- a/tetheroffload/config/1.0/vts/functional/Android.bp
+++ b/tetheroffload/config/1.0/vts/functional/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalTetheroffloadConfigV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
index 02fe96f..6fd9a6f 100644
--- a/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
+++ b/tetheroffload/config/1.0/vts/functional/VtsHalTetheroffloadConfigV1_0TargetTest.cpp
@@ -176,6 +176,7 @@
     ASSERT_TRUE(ret.isOk());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadConfigHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, OffloadConfigHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
diff --git a/tetheroffload/control/1.0/Android.bp b/tetheroffload/control/1.0/Android.bp
index f894448..acb5ee8 100644
--- a/tetheroffload/control/1.0/Android.bp
+++ b/tetheroffload/control/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tetheroffload.control@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IOffloadControl.hal",
diff --git a/tetheroffload/control/1.0/vts/functional/Android.bp b/tetheroffload/control/1.0/vts/functional/Android.bp
index c51dd8b..dc3b00c 100644
--- a/tetheroffload/control/1.0/vts/functional/Android.bp
+++ b/tetheroffload/control/1.0/vts/functional/Android.bp
@@ -12,13 +12,44 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalTetheroffloadControlV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
-    srcs: ["VtsHalTetheroffloadControlV1_0TargetTest.cpp"],
+    local_include_dirs: ["include"],
+    srcs: [
+        "VtsHalTetheroffloadControlV1_0TargetTest.cpp",
+        "OffloadControlTestBase.cpp",
+        "OffloadControlTestUtils.cpp",
+    ],
     static_libs: [
         "android.hardware.tetheroffload.config@1.0",
         "android.hardware.tetheroffload.control@1.0",
     ],
-    test_suites: ["general-tests", "vts"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
+
+cc_test_library {
+    name: "VtsHalTetheroffloadControlV1_0TargetTest-lib",
+    defaults: ["VtsHalTargetTestDefaults"],
+    export_include_dirs: ["include"],
+    static_libs: [
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.tetheroffload.control@1.0",
+    ],
+    srcs: [
+        "OffloadControlTestBase.cpp",
+        "OffloadControlTestUtils.cpp",
+    ],
 }
diff --git a/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp b/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
new file mode 100644
index 0000000..e392e96
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/OffloadControlTestBase.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 <OffloadControlTestBase.h>
+
+void OffloadControlTestBase::TearDown() {
+    // For good measure, the teardown should try stopOffload() once more, since
+    // different HAL call test cycles might enter this function. Also the
+    // return code cannot be actually expected for all cases, hence ignore it.
+    stopOffload(ExpectBoolean::Ignored);
+}
+
+// The IOffloadConfig HAL is tested more thoroughly elsewhere. Here the class
+// just setup everything correctly and verify basic readiness.
+void OffloadControlTestBase::setupConfigHal() {
+    config = IOffloadConfig::getService(std::get<0>(GetParam()));
+    ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance";
+
+    unique_fd fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY));
+    if (fd1.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        FAIL();
+    }
+    native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
+    nativeHandle1->data[0] = fd1.release();
+    hidl_handle h1;
+    h1.setTo(nativeHandle1, true);
+
+    unique_fd fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
+    if (fd2.get() < 0) {
+        ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
+        FAIL();
+    }
+    native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
+    nativeHandle2->data[0] = fd2.release();
+    hidl_handle h2;
+    h2.setTo(nativeHandle2, true);
+
+    const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
+    ASSERT_TRUE(ret.isOk());
+}
+
+void OffloadControlTestBase::stopOffload(const ExpectBoolean value) {
+    auto cb = [&](bool success, const hidl_string& errMsg) {
+        switch (value) {
+            case ExpectBoolean::False:
+                ASSERT_EQ(false, success) << "Unexpectedly able to stop offload: " << errMsg;
+                break;
+            case ExpectBoolean::True:
+                ASSERT_EQ(true, success) << "Unexpectedly failed to stop offload: " << errMsg;
+                break;
+            case ExpectBoolean::Ignored:
+                break;
+        }
+    };
+    const Return<void> ret = control->stopOffload(cb);
+    ASSERT_TRUE(ret.isOk());
+}
diff --git a/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp b/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp
new file mode 100644
index 0000000..c784fe1
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/OffloadControlTestUtils.cpp
@@ -0,0 +1,53 @@
+/*
+ * 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 <OffloadControlTestUtils.h>
+#include <android-base/unique_fd.h>
+
+using android::base::unique_fd;
+
+inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) {
+    return reinterpret_cast<const sockaddr*>(nladdr);
+}
+
+int conntrackSocket(unsigned groups) {
+    unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
+    if (s.get() < 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl bind_addr = {
+            .nl_family = AF_NETLINK,
+            .nl_pad = 0,
+            .nl_pid = 0,
+            .nl_groups = groups,
+    };
+    if (::bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) < 0) {
+        return -errno;
+    }
+
+    const struct sockaddr_nl kernel_addr = {
+            .nl_family = AF_NETLINK,
+            .nl_pad = 0,
+            .nl_pid = 0,
+            .nl_groups = groups,
+    };
+    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
+        return -errno;
+    }
+
+    return s.release();
+}
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
index b422b2f..ea9bcb5 100644
--- a/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
+++ b/tetheroffload/control/1.0/vts/functional/VtsHalTetheroffloadControlV1_0TargetTest.cpp
@@ -16,215 +16,24 @@
 
 #define LOG_TAG "VtsOffloadControlV1_0TargetTest"
 
-#include <VtsHalHidlTargetCallbackBase.h>
+#include <OffloadControlTestV1_0.h>
 #include <android-base/stringprintf.h>
-#include <android-base/unique_fd.h>
-#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
-#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
-#include <android/hardware/tetheroffload/control/1.0/types.h>
 #include <gtest/gtest.h>
 #include <hidl/GtestPrinter.h>
 #include <hidl/ServiceManagement.h>
-#include <linux/netfilter/nfnetlink.h>
-#include <linux/netlink.h>
-#include <log/log.h>
 #include <net/if.h>
 #include <sys/socket.h>
-#include <unistd.h>
-#include <set>
 
 using android::base::StringPrintf;
-using android::base::unique_fd;
-using android::hardware::hidl_handle;
-using android::hardware::hidl_string;
-using android::hardware::hidl_vec;
 using android::hardware::Return;
 using android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
 using android::hardware::tetheroffload::control::V1_0::IOffloadControl;
-using android::hardware::tetheroffload::control::V1_0::IPv4AddrPortPair;
-using android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
-using android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent;
-using android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate;
-using android::hardware::tetheroffload::control::V1_0::NetworkProtocol;
 using android::hardware::Void;
-using android::sp;
-
-enum class ExpectBoolean {
-    Ignored = -1,
-    False = 0,
-    True = 1,
-};
 
 constexpr const char* TEST_IFACE = "rmnet_data0";
 
-// We use #defines here so as to get local lamba captures and error message line numbers
-#define ASSERT_TRUE_CALLBACK                                                    \
-    [&](bool success, std::string errMsg) {                                     \
-        std::string msg = StringPrintf("unexpected error: %s", errMsg.c_str()); \
-        ASSERT_TRUE(success) << msg;                                            \
-    }
-
-#define ASSERT_FALSE_CALLBACK                                                 \
-    [&](bool success, std::string errMsg) {                                   \
-        std::string msg = StringPrintf("expected error: %s", errMsg.c_str()); \
-        ASSERT_FALSE(success) << msg;                                         \
-    }
-
-#define ASSERT_ZERO_BYTES_CALLBACK            \
-    [&](uint64_t rxBytes, uint64_t txBytes) { \
-        EXPECT_EQ(0ULL, rxBytes);             \
-        EXPECT_EQ(0ULL, txBytes);             \
-    }
-
-inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) {
-    return reinterpret_cast<const sockaddr*>(nladdr);
-}
-
-int conntrackSocket(unsigned groups) {
-    unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
-    if (s.get() < 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl bind_addr = {
-        .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups,
-    };
-    if (::bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) < 0) {
-        return -errno;
-    }
-
-    const struct sockaddr_nl kernel_addr = {
-        .nl_family = AF_NETLINK, .nl_pad = 0, .nl_pid = 0, .nl_groups = groups,
-    };
-    if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
-        return -errno;
-    }
-
-    return s.release();
-}
-
-constexpr char kCallbackOnEvent[] = "onEvent";
-constexpr char kCallbackUpdateTimeout[] = "updateTimeout";
-
-class TetheringOffloadCallbackArgs {
-   public:
-    OffloadCallbackEvent last_event;
-    NatTimeoutUpdate last_params;
-};
-
-class OffloadControlHidlTestBase
-    : public testing::TestWithParam<std::tuple<std::string, std::string>> {
-   public:
-    virtual void SetUp() override {
-        setupConfigHal();
-        prepareControlHal();
-    }
-
-    virtual void TearDown() override {
-        // For good measure, we should try stopOffload() once more. Since we
-        // don't know where we are in HAL call test cycle we don't know what
-        // return code to actually expect, so we just ignore it.
-        stopOffload(ExpectBoolean::Ignored);
-    }
-
-    // The IOffloadConfig HAL is tested more thoroughly elsewhere. He we just
-    // setup everything correctly and verify basic readiness.
-    void setupConfigHal() {
-        config = IOffloadConfig::getService(std::get<0>(GetParam()));
-        ASSERT_NE(nullptr, config.get()) << "Could not get HIDL instance";
-
-        unique_fd fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY));
-        if (fd1.get() < 0) {
-            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-            FAIL();
-        }
-        native_handle_t* const nativeHandle1 = native_handle_create(1, 0);
-        nativeHandle1->data[0] = fd1.release();
-        hidl_handle h1;
-        h1.setTo(nativeHandle1, true);
-
-        unique_fd fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
-        if (fd2.get() < 0) {
-            ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
-            FAIL();
-        }
-        native_handle_t* const nativeHandle2 = native_handle_create(1, 0);
-        nativeHandle2->data[0] = fd2.release();
-        hidl_handle h2;
-        h2.setTo(nativeHandle2, true);
-
-        const Return<void> ret = config->setHandles(h1, h2, ASSERT_TRUE_CALLBACK);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    void prepareControlHal() {
-        control = IOffloadControl::getService(std::get<1>(GetParam()));
-        ASSERT_NE(nullptr, control.get()) << "Could not get HIDL instance";
-
-        control_cb = new TetheringOffloadCallback();
-        ASSERT_NE(nullptr, control_cb.get()) << "Could not get get offload callback";
-    }
-
-    void initOffload(const bool expected_result) {
-        auto init_cb = [&](bool success, std::string errMsg) {
-            std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
-                                           success ? "succeeded" : "failed", errMsg.c_str());
-            ASSERT_EQ(expected_result, success) << msg;
-        };
-        const Return<void> ret = control->initOffload(control_cb, init_cb);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    void setupControlHal() {
-        prepareControlHal();
-        initOffload(true);
-    }
-
-    void stopOffload(const ExpectBoolean value) {
-        auto cb = [&](bool success, const hidl_string& errMsg) {
-            switch (value) {
-                case ExpectBoolean::False:
-                    ASSERT_EQ(false, success) << "Unexpectedly able to stop offload: " << errMsg;
-                    break;
-                case ExpectBoolean::True:
-                    ASSERT_EQ(true, success) << "Unexpectedly failed to stop offload: " << errMsg;
-                    break;
-                case ExpectBoolean::Ignored:
-                    break;
-            }
-        };
-        const Return<void> ret = control->stopOffload(cb);
-        ASSERT_TRUE(ret.isOk());
-    }
-
-    // Callback class for both events and NAT timeout updates.
-    class TetheringOffloadCallback
-        : public testing::VtsHalHidlTargetCallbackBase<TetheringOffloadCallbackArgs>,
-          public ITetheringOffloadCallback {
-       public:
-        TetheringOffloadCallback() = default;
-        virtual ~TetheringOffloadCallback() = default;
-
-        Return<void> onEvent(OffloadCallbackEvent event) override {
-            const TetheringOffloadCallbackArgs args{.last_event = event};
-            NotifyFromCallback(kCallbackOnEvent, args);
-            return Void();
-        };
-
-        Return<void> updateTimeout(const NatTimeoutUpdate& params) override {
-            const TetheringOffloadCallbackArgs args{.last_params = params};
-            NotifyFromCallback(kCallbackUpdateTimeout, args);
-            return Void();
-        };
-    };
-
-    sp<IOffloadConfig> config;
-    sp<IOffloadControl> control;
-    sp<TetheringOffloadCallback> control_cb;
-};
-
 // Call initOffload() multiple times. Check that non-first initOffload() calls return false.
-TEST_P(OffloadControlHidlTestBase, AdditionalInitsWithoutStopReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AdditionalInitsWithoutStopReturnFalse) {
     initOffload(true);
     initOffload(false);
     initOffload(false);
@@ -232,7 +41,7 @@
 }
 
 // Check that calling stopOffload() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, MultipleStopsWithoutInitReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, MultipleStopsWithoutInitReturnFalse) {
     stopOffload(ExpectBoolean::False);
     stopOffload(ExpectBoolean::False);
     stopOffload(ExpectBoolean::False);
@@ -251,7 +60,7 @@
 }
 
 // Check that calling stopOffload() after a complete init/stop cycle returns false.
-TEST_P(OffloadControlHidlTestBase, AdditionalStopsWithInitReturnFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AdditionalStopsWithInitReturnFalse) {
     initOffload(true);
     // Call setUpstreamParameters() so that "offload" can be reasonably said
     // to be both requested and operational.
@@ -273,7 +82,7 @@
 }
 
 // Check that calling setLocalPrefixes() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, SetLocalPrefixesWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetLocalPrefixesWithoutInitReturnsFalse) {
     const vector<hidl_string> prefixes{hidl_string("2001:db8::/64")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -281,14 +90,14 @@
 
 // Check that calling getForwardedStats() without first having called initOffload()
 // returns zero bytes statistics.
-TEST_P(OffloadControlHidlTestBase, GetForwardedStatsWithoutInitReturnsZeroValues) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, GetForwardedStatsWithoutInitReturnsZeroValues) {
     const hidl_string upstream(TEST_IFACE);
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Check that calling setDataLimit() without first having called initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, SetDataLimitWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetDataLimitWithoutInitReturnsFalse) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_FALSE_CALLBACK);
@@ -297,7 +106,7 @@
 
 // Check that calling setUpstreamParameters() without first having called initOffload()
 // returns false.
-TEST_P(OffloadControlHidlTestBase, SetUpstreamParametersWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, SetUpstreamParametersWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.0/24");
     const hidl_string v4Gw("192.0.2.1");
@@ -309,7 +118,7 @@
 
 // Check that calling addDownstream() with an IPv4 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, AddIPv4DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AddIPv4DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -318,7 +127,7 @@
 
 // Check that calling addDownstream() with an IPv6 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, AddIPv6DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, AddIPv6DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -327,7 +136,7 @@
 
 // Check that calling removeDownstream() with an IPv4 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, RemoveIPv4DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, RemoveIPv4DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -336,48 +145,33 @@
 
 // Check that calling removeDownstream() with an IPv6 prefix without first having called
 // initOffload() returns false.
-TEST_P(OffloadControlHidlTestBase, RemoveIPv6DownstreamWithoutInitReturnsFalse) {
+TEST_P(OffloadControlTestV1_0_HalNotStarted, RemoveIPv6DownstreamWithoutInitReturnsFalse) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
-class OffloadControlHidlTest : public OffloadControlHidlTestBase {
-   public:
-    virtual void SetUp() override {
-        setupConfigHal();
-        setupControlHal();
-    }
-
-    virtual void TearDown() override {
-        // For good measure, we should try stopOffload() once more. Since we
-        // don't know where we are in HAL call test cycle we don't know what
-        // return code to actually expect, so we just ignore it.
-        stopOffload(ExpectBoolean::Ignored);
-    }
-};
-
 /*
  * Tests for IOffloadControl::setLocalPrefixes().
  */
 
 // Test setLocalPrefixes() accepts an IPv4 address.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv4AddressOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv4AddressOk) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.1")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() accepts an IPv6 address.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv6AddressOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv6AddressOk) {
     const vector<hidl_string> prefixes{hidl_string("fe80::1")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() accepts both IPv4 and IPv6 prefixes.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesIPv4v6PrefixesOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesIPv4v6PrefixesOk) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.0/24"), hidl_string("fe80::/64")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_TRUE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -386,14 +180,14 @@
 // Test that setLocalPrefixes() fails given empty input. There is always
 // a non-empty set of local prefixes; when all networking interfaces are down
 // we still apply {127.0.0.0/8, ::1/128, fe80::/64} here.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesEmptyFails) {
     const vector<hidl_string> prefixes{};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
 }
 
 // Test setLocalPrefixes() fails on incorrectly formed input strings.
-TEST_P(OffloadControlHidlTest, SetLocalPrefixesInvalidFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetLocalPrefixesInvalidFails) {
     const vector<hidl_string> prefixes{hidl_string("192.0.2.0/24"), hidl_string("invalid")};
     const Return<void> ret = control->setLocalPrefixes(prefixes, ASSERT_FALSE_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -404,7 +198,7 @@
  */
 
 // Test that getForwardedStats() for a non-existent upstream yields zero bytes statistics.
-TEST_P(OffloadControlHidlTest, GetForwardedStatsInvalidUpstreamIface) {
+TEST_P(OffloadControlTestV1_0_HalStarted, GetForwardedStatsInvalidUpstreamIface) {
     const hidl_string upstream("invalid");
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -412,7 +206,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, GetForwardedStatsDummyIface) {
+TEST_P(OffloadControlTestV1_0_HalStarted, GetForwardedStatsDummyIface) {
     const hidl_string upstream(TEST_IFACE);
     const Return<void> ret = control->getForwardedStats(upstream, ASSERT_ZERO_BYTES_CALLBACK);
     EXPECT_TRUE(ret.isOk());
@@ -423,7 +217,7 @@
  */
 
 // Test that setDataLimit() for an empty interface name fails.
-TEST_P(OffloadControlHidlTest, SetDataLimitEmptyUpstreamIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitEmptyUpstreamIfaceFails) {
     const hidl_string upstream("");
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_FALSE_CALLBACK);
@@ -432,7 +226,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetDataLimitNonZeroOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitNonZeroOk) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 5000ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_TRUE_CALLBACK);
@@ -441,7 +235,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetDataLimitZeroOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetDataLimitZeroOk) {
     const hidl_string upstream(TEST_IFACE);
     const uint64_t limit = 0ULL;
     const Return<void> ret = control->setDataLimit(upstream, limit, ASSERT_TRUE_CALLBACK);
@@ -454,7 +248,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv6OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv6OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("");
     const hidl_string v4Gw("");
@@ -466,7 +260,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersAlternateIPv6OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersAlternateIPv6OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr;
     const hidl_string v4Gw;
@@ -478,7 +272,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv4OnlyOk) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv4OnlyOk) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -490,7 +284,7 @@
 
 // TEST_IFACE is presumed to exist on the device and be up. No packets
 // are ever actually caused to be forwarded.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersIPv4v6Ok) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersIPv4v6Ok) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -501,7 +295,7 @@
 }
 
 // Test that setUpstreamParameters() fails when all parameters are empty.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersEmptyFails) {
     const hidl_string iface("");
     const hidl_string v4Addr("");
     const hidl_string v4Gw("");
@@ -512,7 +306,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersBogusIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersBogusIfaceFails) {
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -526,7 +320,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv4 addresses.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersInvalidIPv4AddrFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersInvalidIPv4AddrFails) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Gw("192.0.2.1");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -540,7 +334,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv4 gateways.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersInvalidIPv4GatewayFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersInvalidIPv4GatewayFails) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const vector<hidl_string> v6Gws{hidl_string("fe80::db8:1")};
@@ -554,7 +348,7 @@
 }
 
 // Test that setUpstreamParameters() fails when given unparseable IPv6 gateways.
-TEST_P(OffloadControlHidlTest, SetUpstreamParametersBadIPv6GatewaysFail) {
+TEST_P(OffloadControlTestV1_0_HalStarted, SetUpstreamParametersBadIPv6GatewaysFail) {
     const hidl_string iface(TEST_IFACE);
     const hidl_string v4Addr("192.0.2.2");
     const hidl_string v4Gw("192.0.2.1");
@@ -572,7 +366,7 @@
  */
 
 // Test addDownstream() works given an IPv4 prefix.
-TEST_P(OffloadControlHidlTest, AddDownstreamIPv4) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamIPv4) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("192.0.2.0/24");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
@@ -580,7 +374,7 @@
 }
 
 // Test addDownstream() works given an IPv6 prefix.
-TEST_P(OffloadControlHidlTest, AddDownstreamIPv6) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamIPv6) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("2001:db8::/64");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_TRUE_CALLBACK);
@@ -588,7 +382,7 @@
 }
 
 // Test addDownstream() fails given all empty parameters.
-TEST_P(OffloadControlHidlTest, AddDownstreamEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamEmptyFails) {
     const hidl_string iface("");
     const hidl_string prefix("");
     const Return<void> ret = control->addDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -596,7 +390,7 @@
 }
 
 // Test addDownstream() fails given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, AddDownstreamInvalidIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamInvalidIfaceFails) {
     const hidl_string prefix("192.0.2.0/24");
     for (const auto& bogus : {"", "invalid"}) {
         SCOPED_TRACE(StringPrintf("iface='%s'", bogus));
@@ -607,7 +401,7 @@
 }
 
 // Test addDownstream() fails given unparseable prefix arguments.
-TEST_P(OffloadControlHidlTest, AddDownstreamBogusPrefixFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, AddDownstreamBogusPrefixFails) {
     const hidl_string iface("dummy0");
     for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
         SCOPED_TRACE(StringPrintf("prefix='%s'", bogus));
@@ -622,7 +416,7 @@
  */
 
 // Test removeDownstream() works given an IPv4 prefix.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamIPv4) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamIPv4) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("192.0.2.0/24");
     // First add the downstream, otherwise removeDownstream logic can reasonably
@@ -634,7 +428,7 @@
 }
 
 // Test removeDownstream() works given an IPv6 prefix.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamIPv6) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamIPv6) {
     const hidl_string iface("dummy0");
     const hidl_string prefix("2001:db8::/64");
     // First add the downstream, otherwise removeDownstream logic can reasonably
@@ -646,7 +440,7 @@
 }
 
 // Test removeDownstream() fails given all empty parameters.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamEmptyFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamEmptyFails) {
     const hidl_string iface("");
     const hidl_string prefix("");
     const Return<void> ret = control->removeDownstream(iface, prefix, ASSERT_FALSE_CALLBACK);
@@ -654,7 +448,7 @@
 }
 
 // Test removeDownstream() fails given empty or non-existent interface names.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamBogusIfaceFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamBogusIfaceFails) {
     const hidl_string prefix("192.0.2.0/24");
     for (const auto& bogus : {"", "invalid"}) {
         SCOPED_TRACE(StringPrintf("iface='%s'", bogus));
@@ -665,7 +459,7 @@
 }
 
 // Test removeDownstream() fails given unparseable prefix arguments.
-TEST_P(OffloadControlHidlTest, RemoveDownstreamBogusPrefixFails) {
+TEST_P(OffloadControlTestV1_0_HalStarted, RemoveDownstreamBogusPrefixFails) {
     const hidl_string iface("dummy0");
     for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) {
         SCOPED_TRACE(StringPrintf("prefix='%s'", bogus));
@@ -675,21 +469,20 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalNotStarted);
 INSTANTIATE_TEST_CASE_P(
-    PerInstance, OffloadControlHidlTestBase,
-    testing::Combine(
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadControl::descriptor))),
-    android::hardware::PrintInstanceTupleNameToString<>);
+        PerInstance, OffloadControlTestV1_0_HalNotStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_0_HalStarted);
 INSTANTIATE_TEST_CASE_P(
-    PerInstance, OffloadControlHidlTest,
-    testing::Combine(
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadConfig::descriptor)),
-        testing::ValuesIn(
-            android::hardware::getAllHalInstanceNames(IOffloadControl::descriptor))),
-    android::hardware::PrintInstanceTupleNameToString<>);
-
+        PerInstance, OffloadControlTestV1_0_HalStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
new file mode 100644
index 0000000..994c808
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestBase.h
@@ -0,0 +1,108 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <OffloadControlTestUtils.h>
+#include <VtsHalHidlTargetCallbackBase.h>
+#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
+#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
+#include <android/hardware/tetheroffload/control/1.0/IOffloadControl.h>
+#include <android/hardware/tetheroffload/control/1.0/types.h>
+#include <gtest/gtest.h>
+#include <linux/netfilter/nfnetlink.h>
+#include <log/log.h>
+
+using android::sp;
+using android::base::StringPrintf;
+using android::base::unique_fd;
+using android::hardware::hidl_handle;
+using android::hardware::hidl_string;
+using android::hardware::hidl_vec;
+using android::hardware::Return;
+using android::hardware::Void;
+using android::hardware::tetheroffload::config::V1_0::IOffloadConfig;
+using android::hardware::tetheroffload::control::V1_0::ITetheringOffloadCallback;
+using android::hardware::tetheroffload::control::V1_0::NatTimeoutUpdate;
+using android::hardware::tetheroffload::control::V1_0::OffloadCallbackEvent;
+
+constexpr char kCallbackOnEvent[] = "onEvent";
+constexpr char kCallbackUpdateTimeout[] = "updateTimeout";
+
+enum class ExpectBoolean {
+    Ignored = -1,
+    False = 0,
+    True = 1,
+};
+
+class TetheringOffloadCallbackArgs {
+  public:
+    OffloadCallbackEvent last_event;
+    NatTimeoutUpdate last_params;
+};
+
+class OffloadControlTestBase : public testing::TestWithParam<std::tuple<std::string, std::string>> {
+  public:
+    virtual void SetUp() = 0;
+
+    virtual void TearDown();
+
+    // Called once in setup stage to retrieve correct version of
+    // IOffloadControl object.
+    virtual sp<android::hardware::tetheroffload::control::V1_0::IOffloadControl> createControl(
+            const std::string& serviceName) = 0;
+
+    // The IOffloadConfig HAL is tested more thoroughly elsewhere. Here the
+    // class just setup everything correctly and verify basic readiness.
+    void setupConfigHal();
+
+    virtual void prepareControlHal() = 0;
+
+    virtual void initOffload(const bool expected_result) = 0;
+
+    void setupControlHal() {
+        prepareControlHal();
+        initOffload(true);
+    };
+
+    void stopOffload(const ExpectBoolean value);
+
+    // Callback class for both events and NAT timeout updates.
+    class TetheringOffloadCallback
+        : public testing::VtsHalHidlTargetCallbackBase<TetheringOffloadCallbackArgs>,
+          public ITetheringOffloadCallback {
+      public:
+        TetheringOffloadCallback() = default;
+        virtual ~TetheringOffloadCallback() = default;
+
+        Return<void> onEvent(OffloadCallbackEvent event) override {
+            const TetheringOffloadCallbackArgs args{.last_event = event};
+            NotifyFromCallback(kCallbackOnEvent, args);
+            return Void();
+        };
+
+        Return<void> updateTimeout(const NatTimeoutUpdate& params) override {
+            const TetheringOffloadCallbackArgs args{.last_params = params};
+            NotifyFromCallback(kCallbackUpdateTimeout, args);
+            return Void();
+        };
+    };
+
+    sp<IOffloadConfig> config;
+    sp<android::hardware::tetheroffload::control::V1_0::IOffloadControl> control;
+    sp<TetheringOffloadCallback> control_cb;
+};
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h
new file mode 100644
index 0000000..f9e5783
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestUtils.h
@@ -0,0 +1,39 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <linux/netlink.h>
+#include <sys/socket.h>
+
+// We use #defines here so as to get local lamba captures and error message line numbers
+#define ASSERT_TRUE_CALLBACK                                    \
+    [&](bool success, std::string errMsg) {                     \
+        ASSERT_TRUE(success) << "unexpected error: " << errMsg; \
+    }
+
+#define ASSERT_FALSE_CALLBACK \
+    [&](bool success, std::string errMsg) { ASSERT_FALSE(success) << "expected error: " << errMsg; }
+
+#define ASSERT_ZERO_BYTES_CALLBACK            \
+    [&](uint64_t rxBytes, uint64_t txBytes) { \
+        EXPECT_EQ(0ULL, rxBytes);             \
+        EXPECT_EQ(0ULL, txBytes);             \
+    }
+
+inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr);
+
+int conntrackSocket(unsigned groups);
\ No newline at end of file
diff --git a/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
new file mode 100644
index 0000000..9189d71
--- /dev/null
+++ b/tetheroffload/control/1.0/vts/functional/include/OffloadControlTestV1_0.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <OffloadControlTestBase.h>
+
+class OffloadControlTestV1_0_HalNotStarted : public OffloadControlTestBase {
+  public:
+    virtual void SetUp() override {
+        setupConfigHal();
+        // Create tether offload control object without calling its initOffload.
+        prepareControlHal();
+    }
+
+    virtual sp<android::hardware::tetheroffload::control::V1_0::IOffloadControl> createControl(
+            const std::string& serviceName) override {
+        return android::hardware::tetheroffload::control::V1_0::IOffloadControl::getService(
+                serviceName);
+    }
+
+    virtual void prepareControlHal() override {
+        control = createControl(std::get<1>(GetParam()));
+        ASSERT_NE(nullptr, control.get()) << "Could not get HIDL instance";
+
+        control_cb = new TetheringOffloadCallback();
+        ASSERT_NE(nullptr, control_cb.get()) << "Could not get get offload callback";
+    }
+
+    virtual void initOffload(const bool expected_result) override {
+        auto init_cb = [&](bool success, std::string errMsg) {
+            std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
+                                           success ? "succeeded" : "failed", errMsg.c_str());
+            ASSERT_EQ(expected_result, success) << msg;
+        };
+        const Return<void> ret = control->initOffload(control_cb, init_cb);
+        ASSERT_TRUE(ret.isOk());
+    }
+};
+
+class OffloadControlTestV1_0_HalStarted : public OffloadControlTestV1_0_HalNotStarted {
+  public:
+    virtual void SetUp() override {
+        setupConfigHal();
+        setupControlHal();
+    }
+};
diff --git a/tetheroffload/control/1.1/Android.bp b/tetheroffload/control/1.1/Android.bp
new file mode 100644
index 0000000..7871c2c
--- /dev/null
+++ b/tetheroffload/control/1.1/Android.bp
@@ -0,0 +1,29 @@
+// This file is autogenerated by hidl-gen -Landroidbp.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+hidl_interface {
+    name: "android.hardware.tetheroffload.control@1.1",
+    root: "android.hardware",
+    srcs: [
+        "types.hal",
+        "IOffloadControl.hal",
+        "ITetheringOffloadCallback.hal",
+    ],
+    interfaces: [
+        "android.hardware.tetheroffload.control@1.0",
+        "android.hidl.base@1.0",
+    ],
+    apex_available: [
+        "//apex_available:platform", // Used by InProcessTethering
+        "com.android.tethering",
+    ],
+    gen_java: true,
+}
diff --git a/tetheroffload/control/1.1/IOffloadControl.hal b/tetheroffload/control/1.1/IOffloadControl.hal
new file mode 100644
index 0000000..2eb54c8
--- /dev/null
+++ b/tetheroffload/control/1.1/IOffloadControl.hal
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tetheroffload.control@1.1;
+
+import @1.0::IOffloadControl;
+
+/**
+ * Interface used to control the lifecycle of tethering offload. Note that callbacks of 1.1 HAL
+ * can be registered with the existing callback registration methods from 1.0 HAL.
+ */
+interface IOffloadControl extends @1.0::IOffloadControl {
+    /**
+     * Instruct hardware to send callbacks after certain number of bytes have been transferred in
+     * either direction on this upstream interface.
+     *
+     * The specified quota bytes must be applied to all traffic on the given upstream interface.
+     * This includes hardware forwarded traffic, software forwarded traffic, and AP-originated
+     * traffic. IPv4 and IPv6 traffic both count towards the same quota. IP headers are included
+     * in the byte count quota, but, link-layer headers are not.
+     *
+     * This API may only be called while offload is occurring on this upstream. The hardware
+     * management process MUST NOT store the values when offload is not started and applies once
+     * offload is started. This is because the quota values would likely become stale over
+     * time and would not reflect any new traffic that has occurred.
+     *
+     * This API replaces {@link @1.0::IOffloadControl::setDataLimit}, the framework will always
+     * call setDataWarningAndLimit on 1.1 implementations, and setDataLimit on 1.0 implementations.
+     * Thus, no interaction between the two APIs need to be addressed.
+     *
+     * The specified quota bytes MUST replace any previous quotas set by
+     * {@code setDataWarningAndLimit} specified on the same interface. It may be interpreted as
+     * "tell me when either <warningBytes> or <limitBytes> bytes have been transferred
+     * (in either direction), and stop offload when <limitBytes> bytes have been transferred,
+     * starting now and counting from zero on <upstream>."
+     *
+     * Once the {@code warningBytes} is reached, the callback registered in initOffload must be
+     * called with {@code OFFLOAD_WARNING_REACHED} to indicate this event. Once the event fires
+     * for this upstream, no further {@code OFFLOAD_WARNING_REACHED} event will be fired for this
+     * upstream unless this method is called again with the same interface. Note that there is
+     * no need to call initOffload again to resume offload if stopOffload was not called by the
+     * client.
+     *
+     * Similarly, Once the {@code limitBytes} is reached, the callback registered in initOffload
+     * must be called with {@code OFFLOAD_STOPPED_LIMIT_REACHED} to indicate this event. Once
+     * the event fires for this upstream, no further {@code OFFLOAD_STOPPED_LIMIT_REACHED}
+     * event will be fired for this upstream unless this method is called again with the same
+     * interface. However, unlike {@code warningBytes}, when {@code limitBytes} is reached,
+     * all offload must be stopped. If offload is desired again, the hardware management
+     * process must be completely reprogrammed by calling setUpstreamParameters and
+     * addDownstream again.
+     *
+     * Note that when one of the quota bytes is reached, the other one is still considered valid
+     * unless this method is called again with the same interface.
+     *
+     * @param upstream Upstream interface name that quota must apply to.
+     * @param warningBytes The quota of warning, defined as the number of bytes, starting from
+     *                     zero and counting from now.
+     * @param limitBytes The quota of limit, defined as the number of bytes, starting from zero
+     *                   and counting from now.
+     *
+     * @return success true if quota is applied, false otherwise
+     * @return errMsg a human readable string if error has occurred.
+     */
+    setDataWarningAndLimit(string upstream, uint64_t warningBytes, uint64_t limitBytes)
+        generates (bool success, string errMsg);
+};
diff --git a/tetheroffload/control/1.1/ITetheringOffloadCallback.hal b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
new file mode 100644
index 0000000..9c74641
--- /dev/null
+++ b/tetheroffload/control/1.1/ITetheringOffloadCallback.hal
@@ -0,0 +1,33 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tetheroffload.control@1.1;
+
+import @1.0::ITetheringOffloadCallback;
+import OffloadCallbackEvent;
+
+/**
+ * Callback providing information about status of hardware management process
+ * as well as providing a way to keep offloaded connections from timing out.
+ */
+interface ITetheringOffloadCallback extends @1.0::ITetheringOffloadCallback {
+    /**
+     * Called when an asynchronous event is generated by the hardware
+     * management process. Implementations that report events via this callback
+     * should not invoke onEvent of 1.0 HAL.
+     */
+    oneway onEvent_1_1(OffloadCallbackEvent event);
+};
diff --git a/tetheroffload/control/1.1/types.hal b/tetheroffload/control/1.1/types.hal
new file mode 100644
index 0000000..30e6af3
--- /dev/null
+++ b/tetheroffload/control/1.1/types.hal
@@ -0,0 +1,28 @@
+/*
+ * 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.
+ */
+
+package android.hardware.tetheroffload.control@1.1;
+
+import @1.0::OffloadCallbackEvent;
+
+enum OffloadCallbackEvent : @1.0::OffloadCallbackEvent {
+    /**
+     * This event is fired when the quota, applied in setDataWarning, has expired. It is
+     * recommended that the client query for statistics immediately after receiving this event.
+     * Note that hardware acceleration must not be stopped upon receiving this event.
+     */
+    OFFLOAD_WARNING_REACHED = 6,
+};
diff --git a/tetheroffload/control/1.1/vts/functional/Android.bp b/tetheroffload/control/1.1/vts/functional/Android.bp
new file mode 100644
index 0000000..3eea59b
--- /dev/null
+++ b/tetheroffload/control/1.1/vts/functional/Android.bp
@@ -0,0 +1,39 @@
+// 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalTetheroffloadControlV1_1TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalTetheroffloadControlV1_1TargetTest.cpp"],
+    local_include_dirs: ["include"],
+    static_libs: [
+        "android.hardware.tetheroffload.config@1.0",
+        "android.hardware.tetheroffload.control@1.0",
+        "android.hardware.tetheroffload.control@1.1",
+        "VtsHalTetheroffloadControlV1_0TargetTest-lib",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/tetheroffload/control/1.1/vts/functional/VtsHalTetheroffloadControlV1_1TargetTest.cpp b/tetheroffload/control/1.1/vts/functional/VtsHalTetheroffloadControlV1_1TargetTest.cpp
new file mode 100644
index 0000000..b8c9e53
--- /dev/null
+++ b/tetheroffload/control/1.1/vts/functional/VtsHalTetheroffloadControlV1_1TargetTest.cpp
@@ -0,0 +1,78 @@
+/*
+ * 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 <OffloadControlTestV1_1.h>
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using android::hardware::tetheroffload::control::V1_1::IOffloadControl;
+
+const hidl_string TEST_IFACE("rmnet_data0");
+
+// Check that calling setDataWarningAndLimit() without first having called initOffload() returns
+// false.
+TEST_P(OffloadControlTestV1_1_HalNotStarted, SetDataWarningAndLimitWithoutInitReturnsFalse) {
+    const Return<void> ret = getControlV1_1()->setDataWarningAndLimit(TEST_IFACE, 5000ULL, 5000ULL,
+                                                                      ASSERT_FALSE_CALLBACK);
+    EXPECT_TRUE(ret.isOk());
+}
+
+/*
+ * Tests for IOffloadControl::setDataWarningAndLimit().
+ */
+
+// Test that setDataWarningAndLimit() for an empty interface name fails.
+TEST_P(OffloadControlTestV1_1_HalStarted, SetDataWarningAndLimitEmptyUpstreamIfaceFails) {
+    const Return<void> ret = getControlV1_1()->setDataWarningAndLimit(
+            hidl_string(""), 12345ULL, 67890ULL, ASSERT_FALSE_CALLBACK);
+    EXPECT_TRUE(ret.isOk());
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(OffloadControlTestV1_1_HalStarted, SetDataWarningAndLimitNonZeroOk) {
+    const Return<void> ret = getControlV1_1()->setDataWarningAndLimit(TEST_IFACE, 4000ULL, 5000ULL,
+                                                                      ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(ret.isOk());
+}
+
+// TEST_IFACE is presumed to exist on the device and be up. No packets
+// are ever actually caused to be forwarded.
+TEST_P(OffloadControlTestV1_1_HalStarted, SetDataWarningAndLimitZeroOk) {
+    const Return<void> ret =
+            getControlV1_1()->setDataWarningAndLimit(TEST_IFACE, 0ULL, 0ULL, ASSERT_TRUE_CALLBACK);
+    EXPECT_TRUE(ret.isOk());
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_1_HalNotStarted);
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(OffloadControlTestV1_1_HalStarted);
+
+INSTANTIATE_TEST_CASE_P(
+        PerInstance, OffloadControlTestV1_1_HalNotStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
+
+INSTANTIATE_TEST_CASE_P(
+        PerInstance, OffloadControlTestV1_1_HalStarted,
+        testing::Combine(testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadConfig::descriptor)),
+                         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
+                                 IOffloadControl::descriptor))),
+        android::hardware::PrintInstanceTupleNameToString<>);
diff --git a/tetheroffload/control/1.1/vts/functional/include/OffloadControlTestV1_1.h b/tetheroffload/control/1.1/vts/functional/include/OffloadControlTestV1_1.h
new file mode 100644
index 0000000..a3bc1b4
--- /dev/null
+++ b/tetheroffload/control/1.1/vts/functional/include/OffloadControlTestV1_1.h
@@ -0,0 +1,99 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <OffloadControlTestV1_0.h>
+#include <android/hardware/tetheroffload/control/1.1/IOffloadControl.h>
+#include <android/hardware/tetheroffload/control/1.1/ITetheringOffloadCallback.h>
+#include <gtest/gtest.h>
+
+constexpr char kCallbackOnEvent_1_1[] = "onEvent_1_1";
+
+class TetheringOffloadCallbackArgsV1_1 {
+  public:
+    android::hardware::tetheroffload::control::V1_1::OffloadCallbackEvent last_event;
+};
+
+class OffloadControlTestV1_1_HalNotStarted : public OffloadControlTestV1_0_HalNotStarted {
+  public:
+    virtual sp<android::hardware::tetheroffload::control::V1_0::IOffloadControl> createControl(
+            const std::string& serviceName) override {
+        return android::hardware::tetheroffload::control::V1_1::IOffloadControl::getService(
+                serviceName);
+    };
+
+    void prepareControlHal() override {
+        control = createControl(std::get<1>(GetParam()));
+        ASSERT_NE(nullptr, control.get()) << "Could not get HIDL instance";
+
+        control_cb_1_1 = new TetheringOffloadCallbackV1_1();
+        ASSERT_NE(nullptr, control_cb_1_1.get()) << "Could not get offload callback";
+    };
+
+    void initOffload(const bool expected_result) override {
+        auto init_cb = [&](bool success, std::string errMsg) {
+            std::string msg = StringPrintf("Unexpectedly %s to init offload: %s",
+                                           success ? "succeeded" : "failed", errMsg.c_str());
+            ASSERT_EQ(expected_result, success) << msg;
+        };
+        auto control = getControlV1_1();
+        ASSERT_NE(control, nullptr);
+        const Return<void> ret = control->initOffload(control_cb_1_1, init_cb);
+        ASSERT_TRUE(ret.isOk());
+    };
+
+    sp<android::hardware::tetheroffload::control::V1_1::IOffloadControl> getControlV1_1() {
+        // The cast is safe since only devices with V1.1+ HAL will be enumerated and pass in to the
+        // test.
+        return android::hardware::tetheroffload::control::V1_1::IOffloadControl::castFrom(control)
+                .withDefault(nullptr);
+    };
+
+    // Callback class for both new events.
+    class TetheringOffloadCallbackV1_1
+        : public testing::VtsHalHidlTargetCallbackBase<TetheringOffloadCallbackArgsV1_1>,
+          public android::hardware::tetheroffload::control::V1_1::ITetheringOffloadCallback {
+      public:
+        Return<void> onEvent_1_1(
+                android::hardware::tetheroffload::control::V1_1::OffloadCallbackEvent event)
+                override {
+            const TetheringOffloadCallbackArgsV1_1 args{.last_event = event};
+            NotifyFromCallback(kCallbackOnEvent_1_1, args);
+            return Void();
+        };
+
+        Return<void> onEvent([[maybe_unused]] OffloadCallbackEvent event) override {
+            // Tested only in IOffloadControl 1.0.
+            return Void();
+        };
+
+        Return<void> updateTimeout([[maybe_unused]] const NatTimeoutUpdate& params) override {
+            // Tested only in IOffloadControl 1.0.
+            return Void();
+        };
+    };
+
+    sp<TetheringOffloadCallbackV1_1> control_cb_1_1;
+};
+
+class OffloadControlTestV1_1_HalStarted : public OffloadControlTestV1_1_HalNotStarted {
+  public:
+    virtual void SetUp() override {
+        setupConfigHal();
+        setupControlHal();
+    }
+};
\ No newline at end of file
diff --git a/thermal/1.0/Android.bp b/thermal/1.0/Android.bp
index de168d8..f6cf294 100644
--- a/thermal/1.0/Android.bp
+++ b/thermal/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.thermal@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IThermal.hal",
diff --git a/thermal/1.0/default/Android.bp b/thermal/1.0/default/Android.bp
index 194a9f8..18bc046 100644
--- a/thermal/1.0/default/Android.bp
+++ b/thermal/1.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.thermal@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/thermal/1.0/vts/functional/Android.bp b/thermal/1.0/vts/functional/Android.bp
index 5ccf07a..c73008a 100644
--- a/thermal/1.0/vts/functional/Android.bp
+++ b/thermal/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalThermalV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.thermal@1.0"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/thermal/1.0/vts/functional/VtsHalThermalV1_0TargetTest.cpp b/thermal/1.0/vts/functional/VtsHalThermalV1_0TargetTest.cpp
index aa1c0ce..c2be07a 100644
--- a/thermal/1.0/vts/functional/VtsHalThermalV1_0TargetTest.cpp
+++ b/thermal/1.0/vts/functional/VtsHalThermalV1_0TargetTest.cpp
@@ -208,6 +208,7 @@
   }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThermalHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ThermalHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IThermal::descriptor)),
diff --git a/thermal/1.1/Android.bp b/thermal/1.1/Android.bp
index f38ed3b..ef2fa20 100644
--- a/thermal/1.1/Android.bp
+++ b/thermal/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.thermal@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IThermal.hal",
         "IThermalCallback.hal",
diff --git a/thermal/1.1/vts/functional/Android.bp b/thermal/1.1/vts/functional/Android.bp
index b869ece..89fef1b 100644
--- a/thermal/1.1/vts/functional/Android.bp
+++ b/thermal/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalThermalV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/thermal/1.1/vts/functional/VtsHalThermalV1_1TargetTest.cpp b/thermal/1.1/vts/functional/VtsHalThermalV1_1TargetTest.cpp
index bc7b2ee..ec1696f 100644
--- a/thermal/1.1/vts/functional/VtsHalThermalV1_1TargetTest.cpp
+++ b/thermal/1.1/vts/functional/VtsHalThermalV1_1TargetTest.cpp
@@ -100,6 +100,7 @@
     EXPECT_EQ(kThrottleTemp, res.args->temperature);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThermalHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ThermalHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IThermal::descriptor)),
diff --git a/thermal/2.0/Android.bp b/thermal/2.0/Android.bp
index 1b76f37..eb47b38 100644
--- a/thermal/2.0/Android.bp
+++ b/thermal/2.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.thermal@2.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IThermal.hal",
diff --git a/thermal/2.0/default/Android.bp b/thermal/2.0/default/Android.bp
index 7b72694..a63ffbc 100644
--- a/thermal/2.0/default/Android.bp
+++ b/thermal/2.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.thermal@2.0-service.mock",
     defaults: ["hidl_defaults"],
diff --git a/thermal/2.0/vts/functional/Android.bp b/thermal/2.0/vts/functional/Android.bp
index 026cb62..f26c1af 100644
--- a/thermal/2.0/vts/functional/Android.bp
+++ b/thermal/2.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalThermalV2_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -24,4 +33,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
index 75536a6..2ee91f4 100644
--- a/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
+++ b/thermal/2.0/vts/functional/VtsHalThermalV2_0TargetTest.cpp
@@ -257,6 +257,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ThermalHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, ThermalHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IThermal::descriptor)),
diff --git a/tv/cec/1.0/Android.bp b/tv/cec/1.0/Android.bp
index d41a7e7..889399a 100644
--- a/tv/cec/1.0/Android.bp
+++ b/tv/cec/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tv.cec@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IHdmiCec.hal",
diff --git a/tv/cec/1.0/default/Android.bp b/tv/cec/1.0/default/Android.bp
index 239a527..b4053df 100644
--- a/tv/cec/1.0/default/Android.bp
+++ b/tv/cec/1.0/default/Android.bp
@@ -1,14 +1,27 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.tv.cec@1.0-impl",
     defaults: ["hidl_defaults"],
     vendor: true,
     relative_install_path: "hw",
-    srcs: ["HdmiCec.cpp"],
+    srcs: [
+        "HdmiCec.cpp",
+        "HdmiCecDefault.cpp",
+    ],
 
     shared_libs: [
         "libhidlbase",
         "liblog",
         "libbase",
+        "libcutils",
         "libutils",
         "libhardware",
         "android.hardware.tv.cec@1.0",
diff --git a/tv/cec/1.0/default/HdmiCec.cpp b/tv/cec/1.0/default/HdmiCec.cpp
index 171bdfe..74de785 100644
--- a/tv/cec/1.0/default/HdmiCec.cpp
+++ b/tv/cec/1.0/default/HdmiCec.cpp
@@ -20,6 +20,7 @@
 #include <hardware/hardware.h>
 #include <hardware/hdmi_cec.h>
 #include "HdmiCec.h"
+#include "HdmiCecDefault.h"
 
 namespace android {
 namespace hardware {
@@ -390,6 +391,15 @@
     return mDevice->is_connected(mDevice, portId) > 0;
 }
 
+IHdmiCec* getHdmiCecDefault() {
+    HdmiCecDefault* hdmiCecDefault = new HdmiCecDefault();
+    Result result = hdmiCecDefault->init();
+    if (result == Result::SUCCESS) {
+        return hdmiCecDefault;
+    }
+    LOG(ERROR) << "Failed to load default HAL.";
+    return nullptr;
+}
 
 IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) {
     hdmi_cec_device_t* hdmi_cec_device;
@@ -410,7 +420,7 @@
         return new HdmiCec(hdmi_cec_device);
     } else {
         LOG(ERROR) << "Passthrough failed to load legacy HAL.";
-        return nullptr;
+        return getHdmiCecDefault();
     }
 }
 
diff --git a/tv/cec/1.0/default/HdmiCecDefault.cpp b/tv/cec/1.0/default/HdmiCecDefault.cpp
new file mode 100644
index 0000000..299bcf0
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecDefault.cpp
@@ -0,0 +1,461 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "android.hardware.tv.cec@1.0-impl"
+#include <android-base/logging.h>
+
+#include <cutils/properties.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <linux/cec.h>
+#include <linux/ioctl.h>
+#include <poll.h>
+#include <pthread.h>
+#include <sys/eventfd.h>
+#include <algorithm>
+
+#include "HdmiCecDefault.h"
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+// When set to false, all the CEC commands are discarded. True by default after initialization.
+bool mCecEnabled;
+/*
+ * When set to false, HAL does not wake up the system upon receiving <Image View On> or
+ * <Text View On>. True by default after initialization.
+ */
+bool mWakeupEnabled;
+
+int mCecFd;
+int mExitFd;
+pthread_t mEventThread;
+sp<IHdmiCecCallback> mCallback;
+
+HdmiCecDefault::HdmiCecDefault() {
+    mCecFd = -1;
+    mExitFd = -1;
+    mCecEnabled = false;
+    mWakeupEnabled = false;
+    mCallback = nullptr;
+}
+
+HdmiCecDefault::~HdmiCecDefault() {
+    release();
+}
+
+// Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+Return<Result> HdmiCecDefault::addLogicalAddress(CecLogicalAddress addr) {
+    if (addr < CecLogicalAddress::TV || addr >= CecLogicalAddress::BROADCAST) {
+        LOG(ERROR) << "Add logical address failed, Invalid address";
+        return Result::FAILURE_INVALID_ARGS;
+    }
+
+    struct cec_log_addrs cecLogAddrs;
+    int ret = ioctl(mCecFd, CEC_ADAP_G_LOG_ADDRS, &cecLogAddrs);
+    if (ret) {
+        LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
+        return Result::FAILURE_BUSY;
+    }
+
+    cecLogAddrs.cec_version = getCecVersion();
+    cecLogAddrs.vendor_id = getVendorId();
+
+    unsigned int logAddrType = CEC_LOG_ADDR_TYPE_UNREGISTERED;
+    unsigned int allDevTypes = 0;
+    unsigned int primDevType = 0xff;
+    switch (addr) {
+        case CecLogicalAddress::TV:
+            primDevType = CEC_OP_PRIM_DEVTYPE_TV;
+            logAddrType = CEC_LOG_ADDR_TYPE_TV;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_TV;
+            break;
+        case CecLogicalAddress::RECORDER_1:
+        case CecLogicalAddress::RECORDER_2:
+        case CecLogicalAddress::RECORDER_3:
+            primDevType = CEC_OP_PRIM_DEVTYPE_RECORD;
+            logAddrType = CEC_LOG_ADDR_TYPE_RECORD;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_RECORD;
+            break;
+        case CecLogicalAddress::TUNER_1:
+        case CecLogicalAddress::TUNER_2:
+        case CecLogicalAddress::TUNER_3:
+        case CecLogicalAddress::TUNER_4:
+            primDevType = CEC_OP_PRIM_DEVTYPE_TUNER;
+            logAddrType = CEC_LOG_ADDR_TYPE_TUNER;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_TUNER;
+            break;
+        case CecLogicalAddress::PLAYBACK_1:
+        case CecLogicalAddress::PLAYBACK_2:
+        case CecLogicalAddress::PLAYBACK_3:
+            primDevType = CEC_OP_PRIM_DEVTYPE_PLAYBACK;
+            logAddrType = CEC_LOG_ADDR_TYPE_PLAYBACK;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_PLAYBACK;
+            cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_RC_PASSTHRU;
+            break;
+        case CecLogicalAddress::AUDIO_SYSTEM:
+            primDevType = CEC_OP_PRIM_DEVTYPE_AUDIOSYSTEM;
+            logAddrType = CEC_LOG_ADDR_TYPE_AUDIOSYSTEM;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_AUDIOSYSTEM;
+            break;
+        case CecLogicalAddress::FREE_USE:
+            primDevType = CEC_OP_PRIM_DEVTYPE_PROCESSOR;
+            logAddrType = CEC_LOG_ADDR_TYPE_SPECIFIC;
+            allDevTypes = CEC_OP_ALL_DEVTYPE_SWITCH;
+            break;
+        case CecLogicalAddress::UNREGISTERED:
+            cecLogAddrs.flags |= CEC_LOG_ADDRS_FL_ALLOW_UNREG_FALLBACK;
+            break;
+    }
+
+    int logAddrIndex = cecLogAddrs.num_log_addrs;
+
+    cecLogAddrs.num_log_addrs += 1;
+    cecLogAddrs.log_addr[logAddrIndex] = static_cast<cec_logical_address_t>(addr);
+    cecLogAddrs.log_addr_type[logAddrIndex] = logAddrType;
+    cecLogAddrs.primary_device_type[logAddrIndex] = primDevType;
+    cecLogAddrs.all_device_types[logAddrIndex] = allDevTypes;
+    cecLogAddrs.features[logAddrIndex][0] = 0;
+    cecLogAddrs.features[logAddrIndex][1] = 0;
+
+    ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+    if (ret) {
+        LOG(ERROR) << "Add logical address failed, Error = " << strerror(errno);
+        return Result::FAILURE_BUSY;
+    }
+    return Result::SUCCESS;
+}
+
+Return<void> HdmiCecDefault::clearLogicalAddress() {
+    struct cec_log_addrs cecLogAddrs;
+    memset(&cecLogAddrs, 0, sizeof(cecLogAddrs));
+    int ret = ioctl(mCecFd, CEC_ADAP_S_LOG_ADDRS, &cecLogAddrs);
+    if (ret) {
+        LOG(ERROR) << "Clear logical Address failed, Error = " << strerror(errno);
+    }
+    return Void();
+}
+
+Return<void> HdmiCecDefault::getPhysicalAddress(getPhysicalAddress_cb callback) {
+    uint16_t addr;
+    int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+    if (ret) {
+        LOG(ERROR) << "Get physical address failed, Error = " << strerror(errno);
+        callback(Result::FAILURE_INVALID_STATE, addr);
+        return Void();
+    }
+    callback(Result::SUCCESS, addr);
+    return Void();
+}
+
+Return<SendMessageResult> HdmiCecDefault::sendMessage(const CecMessage& message) {
+    if (!mCecEnabled) {
+        return SendMessageResult::FAIL;
+    }
+
+    struct cec_msg cecMsg;
+    memset(&cecMsg, 0, sizeof(cec_msg));
+
+    int initiator = static_cast<cec_logical_address_t>(message.initiator);
+    int destination = static_cast<cec_logical_address_t>(message.destination);
+
+    cecMsg.msg[0] = (initiator << 4) | destination;
+    for (size_t i = 0; i < message.body.size(); ++i) {
+        cecMsg.msg[i + 1] = message.body[i];
+    }
+    cecMsg.len = message.body.size() + 1;
+
+    int ret = ioctl(mCecFd, CEC_TRANSMIT, &cecMsg);
+
+    if (ret) {
+        LOG(ERROR) << "Send message failed, Error = " << strerror(errno);
+        return SendMessageResult::FAIL;
+    }
+
+    if (cecMsg.tx_status != CEC_TX_STATUS_OK) {
+        LOG(ERROR) << "Send message tx_status = " << cecMsg.tx_status;
+    }
+
+    switch (cecMsg.tx_status) {
+        case CEC_TX_STATUS_OK:
+            return SendMessageResult::SUCCESS;
+        case CEC_TX_STATUS_ARB_LOST:
+            return SendMessageResult::BUSY;
+        case CEC_TX_STATUS_NACK:
+            return SendMessageResult::NACK;
+        default:
+            return SendMessageResult::FAIL;
+    }
+}
+
+Return<void> HdmiCecDefault::setCallback(const sp<IHdmiCecCallback>& callback) {
+    if (mCallback != nullptr) {
+        mCallback->unlinkToDeath(this);
+        mCallback = nullptr;
+    }
+
+    if (callback != nullptr) {
+        mCallback = callback;
+        mCallback->linkToDeath(this, 0 /*cookie*/);
+    }
+    return Void();
+}
+
+Return<int32_t> HdmiCecDefault::getCecVersion() {
+    return property_get_int32("ro.hdmi.cec_version", CEC_OP_CEC_VERSION_1_4);
+}
+
+Return<uint32_t> HdmiCecDefault::getVendorId() {
+    return property_get_int32("ro.hdmi.vendor_id", 0x000c03 /* HDMI LLC vendor ID */);
+}
+
+Return<void> HdmiCecDefault::getPortInfo(getPortInfo_cb callback) {
+    uint16_t addr;
+    int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+    if (ret) {
+        LOG(ERROR) << "Get port info failed, Error = " << strerror(errno);
+    }
+
+    unsigned int type = property_get_int32("ro.hdmi.device_type", CEC_DEVICE_PLAYBACK);
+    hidl_vec<HdmiPortInfo> portInfos(1);
+    portInfos[0] = {.type = (type == CEC_DEVICE_TV ? HdmiPortType::INPUT : HdmiPortType::OUTPUT),
+                    .portId = 1,
+                    .cecSupported = true,
+                    .arcSupported = false,
+                    .physicalAddress = addr};
+    callback(portInfos);
+    return Void();
+}
+
+Return<void> HdmiCecDefault::setOption(OptionKey key, bool value) {
+    switch (key) {
+        case OptionKey::ENABLE_CEC:
+            LOG(DEBUG) << "setOption: Enable CEC: " << value;
+            mCecEnabled = value;
+            break;
+        case OptionKey::WAKEUP:
+            LOG(DEBUG) << "setOption: WAKEUP: " << value;
+            mWakeupEnabled = value;
+            break;
+        default:
+            break;
+    }
+    return Void();
+}
+
+Return<void> HdmiCecDefault::setLanguage(const hidl_string& /*language*/) {
+    return Void();
+}
+
+Return<void> HdmiCecDefault::enableAudioReturnChannel(int32_t /*portId*/, bool /*enable*/) {
+    return Void();
+}
+
+Return<bool> HdmiCecDefault::isConnected(int32_t /*portId*/) {
+    uint16_t addr;
+    int ret = ioctl(mCecFd, CEC_ADAP_G_PHYS_ADDR, &addr);
+    if (ret) {
+        LOG(ERROR) << "Is connected failed, Error = " << strerror(errno);
+        return false;
+    }
+    if (addr == CEC_PHYS_ADDR_INVALID) {
+        return false;
+    }
+    return true;
+}
+
+// Initialise the cec file descriptor
+Return<Result> HdmiCecDefault::init() {
+    const char* path = "/dev/cec0";
+    mCecFd = open(path, O_RDWR);
+    if (mCecFd < 0) {
+        LOG(ERROR) << "Failed to open " << path << ", Error = " << strerror(errno);
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+    mExitFd = eventfd(0, EFD_NONBLOCK);
+    if (mExitFd < 0) {
+        LOG(ERROR) << "Failed to open eventfd, Error = " << strerror(errno);
+        release();
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+
+    // Ensure the CEC device supports required capabilities
+    struct cec_caps caps = {};
+    int ret = ioctl(mCecFd, CEC_ADAP_G_CAPS, &caps);
+    if (ret) {
+        LOG(ERROR) << "Unable to query cec adapter capabilities, Error = " << strerror(errno);
+        release();
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+
+    if (!(caps.capabilities & (CEC_CAP_LOG_ADDRS | CEC_CAP_TRANSMIT | CEC_CAP_PASSTHROUGH))) {
+        LOG(ERROR) << "Wrong cec adapter capabilities " << caps.capabilities;
+        release();
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+
+    uint32_t mode = CEC_MODE_INITIATOR | CEC_MODE_EXCL_FOLLOWER_PASSTHRU;
+    ret = ioctl(mCecFd, CEC_S_MODE, &mode);
+    if (ret) {
+        LOG(ERROR) << "Unable to set initiator mode, Error = " << strerror(errno);
+        release();
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+
+    /* thread loop for receiving cec messages and hotplug events*/
+    if (pthread_create(&mEventThread, NULL, event_thread, NULL)) {
+        LOG(ERROR) << "Can't create event thread: " << strerror(errno);
+        release();
+        return Result::FAILURE_NOT_SUPPORTED;
+    }
+
+    mCecEnabled = true;
+    mWakeupEnabled = true;
+    return Result::SUCCESS;
+}
+
+Return<void> HdmiCecDefault::release() {
+    if (mExitFd > 0) {
+        uint64_t tmp = 1;
+        write(mExitFd, &tmp, sizeof(tmp));
+        pthread_join(mEventThread, NULL);
+    }
+    if (mExitFd > 0) {
+        close(mExitFd);
+    }
+    if (mCecFd > 0) {
+        close(mCecFd);
+    }
+    mCecEnabled = false;
+    mWakeupEnabled = false;
+    setCallback(nullptr);
+    return Void();
+}
+
+void* HdmiCecDefault::event_thread(void*) {
+    struct pollfd ufds[3] = {
+            {mCecFd, POLLIN, 0},
+            {mCecFd, POLLERR, 0},
+            {mExitFd, POLLIN, 0},
+    };
+
+    while (1) {
+        ufds[0].revents = 0;
+        ufds[1].revents = 0;
+        ufds[2].revents = 0;
+
+        int ret = poll(ufds, /* size(ufds) = */ 3, /* timeout = */ -1);
+
+        if (ret <= 0) {
+            continue;
+        }
+
+        if (ufds[2].revents == POLLIN) { /* Exit */
+            break;
+        }
+
+        if (ufds[1].revents == POLLERR) { /* CEC Event */
+            struct cec_event ev;
+            ret = ioctl(mCecFd, CEC_DQEVENT, &ev);
+
+            if (!mCecEnabled) {
+                continue;
+            }
+
+            if (ret) {
+                LOG(ERROR) << "CEC_DQEVENT failed, Error = " << strerror(errno);
+                continue;
+            }
+
+            if (ev.event == CEC_EVENT_STATE_CHANGE) {
+                if (mCallback != nullptr) {
+                    HotplugEvent hotplugEvent{
+                            .connected = (ev.state_change.phys_addr != CEC_PHYS_ADDR_INVALID),
+                            .portId = 1};
+                    mCallback->onHotplugEvent(hotplugEvent);
+                } else {
+                    LOG(ERROR) << "No event callback for hotplug";
+                }
+            }
+        }
+
+        if (ufds[0].revents == POLLIN) { /* CEC Driver */
+            struct cec_msg msg = {};
+            ret = ioctl(mCecFd, CEC_RECEIVE, &msg);
+
+            if (!mCecEnabled) {
+                continue;
+            }
+
+            if (ret) {
+                LOG(ERROR) << "CEC_RECEIVE failed, Error = " << strerror(errno);
+                continue;
+            }
+
+            if (msg.rx_status != CEC_RX_STATUS_OK) {
+                LOG(ERROR) << "msg rx_status = " << msg.rx_status;
+                continue;
+            }
+
+            if (!mWakeupEnabled && isWakeupMessage(msg)) {
+                LOG(DEBUG) << "Filter wakeup message";
+                continue;
+            }
+
+            if (mCallback != nullptr) {
+                size_t length = std::min(msg.len - 1, (uint32_t)MaxLength::MESSAGE_BODY);
+                CecMessage cecMessage{
+                        .initiator = static_cast<CecLogicalAddress>(msg.msg[0] >> 4),
+                        .destination = static_cast<CecLogicalAddress>(msg.msg[0] & 0xf),
+                };
+                cecMessage.body.resize(length);
+                for (size_t i = 0; i < length; ++i) {
+                    cecMessage.body[i] = static_cast<uint8_t>(msg.msg[i + 1]);
+                }
+                mCallback->onCecMessage(cecMessage);
+            } else {
+                LOG(ERROR) << "no event callback for message";
+            }
+        }
+    }
+    return NULL;
+}
+
+int HdmiCecDefault::getOpcode(struct cec_msg message) {
+    return (static_cast<uint8_t>(message.msg[1]) & 0xff);
+}
+
+bool HdmiCecDefault::isWakeupMessage(struct cec_msg message) {
+    int opcode = getOpcode(message);
+    switch (opcode) {
+        case CEC_MESSAGE_TEXT_VIEW_ON:
+        case CEC_MESSAGE_IMAGE_VIEW_ON:
+            return true;
+        default:
+            return false;
+    }
+}
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace cec
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/cec/1.0/default/HdmiCecDefault.h b/tv/cec/1.0/default/HdmiCecDefault.h
new file mode 100644
index 0000000..c1bb2c7
--- /dev/null
+++ b/tv/cec/1.0/default/HdmiCecDefault.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+#include <hardware/hdmi_cec.h>
+
+namespace android {
+namespace hardware {
+namespace tv {
+namespace cec {
+namespace V1_0 {
+namespace implementation {
+
+struct HdmiCecDefault : public IHdmiCec, public hidl_death_recipient {
+    HdmiCecDefault();
+    ~HdmiCecDefault();
+    // Methods from ::android::hardware::tv::cec::V1_0::IHdmiCec follow.
+    Return<Result> addLogicalAddress(CecLogicalAddress addr) override;
+    Return<void> clearLogicalAddress() override;
+    Return<void> getPhysicalAddress(getPhysicalAddress_cb _hidl_cb) override;
+    Return<SendMessageResult> sendMessage(const CecMessage& message) override;
+    Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
+    Return<int32_t> getCecVersion() override;
+    Return<uint32_t> getVendorId() override;
+    Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
+    Return<void> setOption(OptionKey key, bool value) override;
+    Return<void> setLanguage(const hidl_string& language) override;
+    Return<void> enableAudioReturnChannel(int32_t portId, bool enable) override;
+    Return<bool> isConnected(int32_t portId) override;
+
+    virtual void serviceDied(uint64_t, const wp<::android::hidl::base::V1_0::IBase>&) {
+        setCallback(nullptr);
+    }
+
+    Return<Result> init();
+    Return<void> release();
+    static void* event_thread(void*);
+    static int getOpcode(struct cec_msg message);
+    static bool isWakeupMessage(struct cec_msg message);
+};
+
+}  // namespace implementation
+}  // namespace V1_0
+}  // namespace cec
+}  // namespace tv
+}  // namespace hardware
+}  // namespace android
diff --git a/tv/cec/1.0/vts/functional/Android.bp b/tv/cec/1.0/vts/functional/Android.bp
new file mode 100644
index 0000000..9a2c714
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalTvCecV1_0TargetTest",
+    defaults: ["VtsHalTargetTestDefaults"],
+    srcs: ["VtsHalTvCecV1_0TargetTest.cpp"],
+    static_libs: [
+        "android.hardware.tv.cec@1.0",
+    ],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+    disable_framework: true,
+}
diff --git a/tv/cec/1.0/vts/functional/README.md b/tv/cec/1.0/vts/functional/README.md
new file mode 100644
index 0000000..aecd6a6
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/README.md
@@ -0,0 +1,30 @@
+# CEC VTS testing for Android TV devices
+
+Validate HDMI CEC VTS (android.hardware.tv.cec@1.0) functionality.
+
+### Setup:
+
+Running these CEC VTS tests requires an Android playback, TV or audio device connected to the host machine.
+
+![drawing](setup.png)
+
+### Building
+
+From the Android root folder, after choosing the lunch combo, use `make vts` to build VTS.
+
+### Automation
+
+On the host machine, ensure that the [software requirements](https://codelabs.developers.google.com/codelabs/android-lab/#2) for python SDK are met.
+
+Given the setup described above you can run tests with any of the following commands:
+
+1. Using vts-tradefed :
+```
+cd $ANDROID_BUILD_TOP/out/host/linux-x86/vts/android-vts/tools
+./vts-tradefed run commandAndExit vts -m VtsHalTvCecV1_0TargetTest
+```
+2. Using atest
+```
+atest VtsHalTvCecV1_0TargetTest
+```
+Note : atest internally handles building as well. To update the test use '-c' (clear cache) option
diff --git a/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
new file mode 100644
index 0000000..7b42689
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/VtsHalTvCecV1_0TargetTest.cpp
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "HdmiCec_hal_test"
+#include <android-base/logging.h>
+
+#include <android/hardware/tv/cec/1.0/IHdmiCec.h>
+#include <android/hardware/tv/cec/1.0/types.h>
+#include <utils/Log.h>
+#include <sstream>
+#include <vector>
+
+#include <gtest/gtest.h>
+#include <hidl/GtestPrinter.h>
+#include <hidl/ServiceManagement.h>
+
+using ::android::sp;
+using ::android::hardware::hidl_death_recipient;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::Return;
+using ::android::hardware::tv::cec::V1_0::CecDeviceType;
+using ::android::hardware::tv::cec::V1_0::CecLogicalAddress;
+using ::android::hardware::tv::cec::V1_0::CecMessage;
+using ::android::hardware::tv::cec::V1_0::HdmiPortInfo;
+using ::android::hardware::tv::cec::V1_0::HdmiPortType;
+using ::android::hardware::tv::cec::V1_0::IHdmiCec;
+using ::android::hardware::tv::cec::V1_0::OptionKey;
+using ::android::hardware::tv::cec::V1_0::Result;
+using ::android::hardware::tv::cec::V1_0::SendMessageResult;
+
+#define CEC_VERSION 0x05
+#define INCORRECT_VENDOR_ID 0x00
+#define TV_PHYSICAL_ADDRESS 0x0000
+
+// The main test class for TV CEC HAL.
+class HdmiCecTest : public ::testing::TestWithParam<std::string> {
+  public:
+    void SetUp() override {
+        hdmiCec = IHdmiCec::getService(GetParam());
+        ASSERT_NE(hdmiCec, nullptr);
+        ALOGI("%s: getService() for hdmiCec is %s", __func__,
+              hdmiCec->isRemote() ? "remote" : "local");
+
+        hdmiCec_death_recipient = new HdmiCecDeathRecipient();
+        ASSERT_NE(hdmiCec_death_recipient, nullptr);
+        ASSERT_TRUE(hdmiCec->linkToDeath(hdmiCec_death_recipient, 0).isOk());
+    }
+
+    std::vector<int> getDeviceTypes() {
+        std::vector<int> deviceTypes;
+        FILE* p = popen("getprop ro.hdmi.device_type", "re");
+        if (p) {
+            char* line = NULL;
+            size_t len = 0;
+            if (getline(&line, &len, p) > 0) {
+                std::istringstream stream(line);
+                std::string number{};
+                while (std::getline(stream, number, ',')) {
+                    deviceTypes.push_back(stoi(number));
+                }
+            }
+            pclose(p);
+        }
+        return deviceTypes;
+    }
+
+    bool hasDeviceType(CecDeviceType type) {
+        std::vector<int> deviceTypes = getDeviceTypes();
+        for (auto deviceType = deviceTypes.begin(); deviceType != deviceTypes.end(); ++deviceType) {
+            if (*deviceType == (int)type) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    class HdmiCecDeathRecipient : public hidl_death_recipient {
+      public:
+        void serviceDied(uint64_t /*cookie*/,
+                         const android::wp<::android::hidl::base::V1_0::IBase>& /*who*/) override {
+            FAIL();
+        }
+    };
+
+    sp<IHdmiCec> hdmiCec;
+    sp<HdmiCecDeathRecipient> hdmiCec_death_recipient;
+};
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HdmiCecTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, HdmiCecTest,
+        testing::ValuesIn(android::hardware::getAllHalInstanceNames(IHdmiCec::descriptor)),
+        android::hardware::PrintInstanceNameToString);
+
+TEST_P(HdmiCecTest, ClearAddLogicalAddress) {
+    hdmiCec->clearLogicalAddress();
+    Return<Result> ret = hdmiCec->addLogicalAddress(CecLogicalAddress::PLAYBACK_3);
+    EXPECT_EQ(ret, Result::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, PhysicalAddress) {
+    Result result;
+    uint16_t addr;
+    Return<void> ret = hdmiCec->getPhysicalAddress([&result, &addr](Result res, uint16_t paddr) {
+        result = res;
+        addr = paddr;
+    });
+    EXPECT_TRUE(ret.isOk());
+    EXPECT_EQ(result, Result::SUCCESS);
+    if (!hasDeviceType(CecDeviceType::TV)) {
+        EXPECT_NE(addr, TV_PHYSICAL_ADDRESS);
+    }
+}
+
+TEST_P(HdmiCecTest, SendMessage) {
+    CecMessage message;
+    message.initiator = CecLogicalAddress::PLAYBACK_1;
+    message.destination = CecLogicalAddress::BROADCAST;
+    message.body.resize(1);
+    message.body[0] = 131;
+    SendMessageResult ret = hdmiCec->sendMessage(message);
+    EXPECT_EQ(ret, SendMessageResult::SUCCESS);
+}
+
+TEST_P(HdmiCecTest, CecVersion) {
+    Return<int32_t> ret = hdmiCec->getCecVersion();
+    EXPECT_GE(ret, CEC_VERSION);
+}
+
+TEST_P(HdmiCecTest, VendorId) {
+    Return<uint32_t> ret = hdmiCec->getVendorId();
+    EXPECT_NE(ret, INCORRECT_VENDOR_ID);
+}
+
+TEST_P(HdmiCecTest, GetPortInfo) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    bool cecSupportedOnDevice = false;
+    for (size_t i = 0; i < ports.size(); ++i) {
+        EXPECT_TRUE((ports[i].type == HdmiPortType::OUTPUT) ||
+                    (ports[i].type == HdmiPortType::INPUT));
+        if (ports[i].portId == 0) {
+            ALOGW("%s: Port id should start from 1", __func__);
+        }
+        cecSupportedOnDevice = cecSupportedOnDevice | ports[i].cecSupported;
+    }
+    EXPECT_NE(cecSupportedOnDevice, false) << "At least one port should support CEC";
+}
+
+TEST_P(HdmiCecTest, SetOption) {
+    Return<void> ret;
+    ret = hdmiCec->setOption(OptionKey::WAKEUP, false);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, false);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, true);
+    EXPECT_TRUE(ret.isOk());
+    // Restore option keys to their default values
+    ret = hdmiCec->setOption(OptionKey::WAKEUP, true);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::ENABLE_CEC, true);
+    EXPECT_TRUE(ret.isOk());
+    ret = hdmiCec->setOption(OptionKey::SYSTEM_CEC_CONTROL, false);
+    EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, SetLanguage) {
+    Return<void> ret = hdmiCec->setLanguage("eng");
+    EXPECT_TRUE(ret.isOk());
+}
+
+TEST_P(HdmiCecTest, EnableAudioReturnChannel) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    for (size_t i = 0; i < ports.size(); ++i) {
+        if (ports[i].arcSupported) {
+            ret = hdmiCec->enableAudioReturnChannel(ports[i].portId, true);
+            EXPECT_TRUE(ret.isOk());
+        }
+    }
+}
+
+TEST_P(HdmiCecTest, IsConnected) {
+    hidl_vec<HdmiPortInfo> ports;
+    Return<void> ret =
+            hdmiCec->getPortInfo([&ports](hidl_vec<HdmiPortInfo> list) { ports = list; });
+    EXPECT_TRUE(ret.isOk());
+    for (size_t i = 0; i < ports.size(); ++i) {
+        Return<bool> ret = hdmiCec->isConnected(ports[i].portId);
+        EXPECT_TRUE(ret.isOk());
+    }
+}
diff --git a/tv/cec/1.0/vts/functional/setup.png b/tv/cec/1.0/vts/functional/setup.png
new file mode 100644
index 0000000..a64b86c
--- /dev/null
+++ b/tv/cec/1.0/vts/functional/setup.png
Binary files differ
diff --git a/tv/cec/2.0/Android.bp b/tv/cec/2.0/Android.bp
deleted file mode 100644
index 61450ac..0000000
--- a/tv/cec/2.0/Android.bp
+++ /dev/null
@@ -1,19 +0,0 @@
-// This file is autogenerated by hidl-gen -Landroidbp.
-
-hidl_interface {
-    name: "android.hardware.tv.cec@2.0",
-    root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
-    srcs: [
-        "types.hal",
-        "IHdmiCec.hal",
-        "IHdmiCecCallback.hal",
-    ],
-    interfaces: [
-        "android.hidl.base@1.0",
-        "android.hidl.safe_union@1.0",
-    ],
-    gen_java: true,
-}
diff --git a/tv/cec/2.0/IHdmiCec.hal b/tv/cec/2.0/IHdmiCec.hal
deleted file mode 100644
index 0723bad..0000000
--- a/tv/cec/2.0/IHdmiCec.hal
+++ /dev/null
@@ -1,209 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-package android.hardware.tv.cec@2.0;
-
-import IHdmiCecCallback;
-
-/**
- * HDMI-CEC HAL interface definition.
- */
-interface IHdmiCec {
-    /**
-     * Passes Primary Device Type that must be used in this system.
-     *
-     * HAL must use it to allocate logical address as specified in CEC section
-     * 11.3.2 of the CEC spec 2.0b. Then CEC commands addressed the given
-     * logical address can be filtered in.
-     * This method shall be able to be called up to twice to support two Primary
-     * Device Type as specified in CEC Table 11-8 of the CEC spec 2.0b.
-     *
-     * @param deviceType that must be used in this system. It must be a valid
-     *        value in CecDeviceType for the call to succeed.
-     * @return result Result status of the operation. SUCCESS if successful,
-     *         FAILURE_INVALID_ARGS if the given device type is invalid,
-     *         FAILURE_BUSY if device or resource is busy
-     */
-    @callflow(next={"*"})
-    addDeviceType(CecDeviceType deviceType) generates (Result result);
-
-    /**
-     * Clears all Primary Device Types.
-     *
-     * It is used when the system plan to reconfigure Primary Device Type,
-     * hence to tell HAL to release all logical address associated to them,
-     * and change the state back to the beginning.
-     */
-    @callflow(next="addDeviceType")
-    @exit
-    clearDeviceTypes();
-
-    /**
-     * Set All Device Types for a Primary Device Type.
-     *
-     * This value must be used in REPORT_FEATURES message to response
-     * GIVE_FEATURES message in HAL.
-     *
-     * @param allDeviceTypes device all device types for a Primary Device Type.
-     */
-    @callflow(next="addDeviceType")
-    setAllDeviceTypes(CecAllDeviceTypes allDeviceTypes);
-
-    /**
-     * Set Device Features for a Primary Device Type.
-     *
-     * This value must be used in REPORT_FEATURES message to response
-     * GIVE_FEATURES message in HAL.
-     *
-     * @param deviceType The device Primary Device Type.
-     * @param deviceFeatures device features for a Primary Device Type.
-     */
-    @callflow(next="addDeviceType")
-    setDeviceFeatures(CecDeviceType deviceType,
-                      CecDeviceFeatures deviceFeatures);
-
-    /**
-     * Set Remote Control Profile for a Primary Device Type.
-     *
-     * This value must be used in REPORT_FEATURES message to response
-     * GIVE_FEATURES message in HAL.
-     *
-     * @param deviceType The device Primary Device Type.
-     * @param rcProliles remote control profiles for a Primary Device Type.
-     */
-    @callflow(next="addDeviceType")
-    setRcProfile(CecDeviceType deviceType, CecRcProfile rcProfile);
-
-    /**
-     * Retrieve CEC device information.
-     *
-     * CEC section 11.3 of the CEC spec 2.0b specify that a device should not
-     * ask for static information that another device has already supplied.
-     * Therefore, CEC 2.0 software stack need a map to store all cec
-     * devices’ information of current CEC network.
-     * The device information is broadcasted by a device after it allocates a
-     * logical address.  Messages used to send out these information are
-     * REPORT_FEATURES, REPORT_PHYSICAL_ADDRESS, DEVICE_VENDOR_ID.
-     * The spec also requires less than 1 second between REPORT_FEATURES and
-     * REPORT_PHYSICAL_ADDRESS message, and less than 2 second between
-     * REPORT_PHYSICAL_ADDRESS and DEVICE_VENDOR_ID. An Implementation of
-     * device information map in hal can help to meet the timing constraints.
-     * Logical addressing is part of the process to build this map, so the
-     * implementation shall include allocating logical address too.
-     * Whenever a device plug/unplug, the topology of CEC network changes.
-     * The hal implementation shall update devices’ information map, and
-     * send out onTopologyEvent to Android system. Then Android system
-     * will use readDeviceInfo to retreive latest devices’ information of CEC
-     * network.
-     * If SYSTEM_CEC_CONTROL is false, the hal implementation need continue to
-     * maintain and update device information map, and send out pending
-     * onTopologyEvent to Android system when SYSTEM_CEC_CONTROL is
-     * changed to true.
-     *
-     * @param logicalAddress logical address of CEC device.
-     * @param physicalAddress physical address of CEC device.
-     * @return CecDeviceInfo from device information map.
-     * @return result Result status of the operation. SUCCESS if successful,
-     *         FAILURE_INVALID_ARGS if logical or physical address is invalid.
-     *         FAILURE_INVALID_STATE if device information isn't available yet.
-     */
-    @callflow(next="onTopologyChangeEvent")
-    readDeviceInfo(CecLogicalAddress logicalAddress,
-                   CecPhysicalAddress physicalAddress)
-        generates (Result result, CecDeviceInfo deviceInfo);
-
-   /**
-     * Transmits HDMI-CEC message to other HDMI device.
-     *
-     * The method must be designed to return in a certain amount of time and not
-     * hanging forever. This method MUST complete with in 1 second.
-     *
-     * It must try retransmission at least once as specified in the section '7.1
-     * Frame Re-transmissions' of the CEC Spec 1.4b.
-     *
-     * @param message CEC message to be sent to other HDMI device.
-     * @return result Result status of the operation. SUCCESS if successful,
-     *         NACK if the sent message is not acknowledged,
-     *         BUSY if the CEC bus is busy.
-     */
-    @callflow(next="*")
-    sendMessage(CecMessage message) generates (SendMessageResult result);
-
-    /**
-     * Set the callback
-     *
-     * It is used by the framework to receive CecMessages, HDMI hotplug event
-     * and topology update event. Only one callback client is supported.
-     *
-     * @param callback Callback object to pass hdmi events to the system. The
-     *        previously registered callback must be replaced with this one.
-     */
-    @callflow(next={"*"})
-    @entry
-    setCallback(IHdmiCecCallback callback);
-
-   /**
-     * Gets the hdmi port information of underlying hardware.
-     *
-     * @return infos The list of HDMI port information
-     */
-    @callflow(next={"*"})
-    getPortInfo() generates (vec<HdmiPortInfo> infos);
-
-    /**
-     * Sets flags controlling the way HDMI-CEC service works down to HAL
-     * implementation. Those flags must be used in case the feature needs update
-     * in HAL itself, firmware or microcontroller.
-     *
-     * @param key The key of the option to be updated with a new value.
-     * @param value Value to be set.
-     */
-    @callflow(next="*")
-    setOption(OptionKey key, bool value);
-
-    /**
-     * Passes the updated language information of Android system. Contains
-     * three-letter code as defined in ISO/FDIS 639-2. Must be used for HAL to
-     * respond to <Get Menu Language> while in standby mode.
-     *
-     * @param language Three-letter code defined in ISO/FDIS 639-2. Must be
-     *        lowercase letters. (e.g., eng for English)
-     */
-    @callflow(next="*")
-    setLanguage(string language);
-
-    /**
-     * Configures ARC circuit in the hardware logic to start or stop the
-     * feature.
-     *
-     * @param portId Port id to be configured.
-     * @param enable Flag must be either true to start the feature or false to
-     *        stop it.
-     */
-    @callflow(next="*")
-    enableAudioReturnChannel(HdmiPortId portId, bool enable);
-
-    /**
-     * Gets the connection status of the specified port.
-     *
-     * It's specified in CEC section 10.8 of the CEC spec 2.0b
-     *
-     * @param portId Port id to be inspected for the connection status.
-     * @return status True if a device is connected, otherwise false.
-     */
-    @callflow(next="*")
-    isConnected(HdmiPortId portId) generates (bool connected);
-};
diff --git a/tv/cec/2.0/IHdmiCecCallback.hal b/tv/cec/2.0/IHdmiCecCallback.hal
deleted file mode 100644
index 1a8a489..0000000
--- a/tv/cec/2.0/IHdmiCecCallback.hal
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-package android.hardware.tv.cec@2.0;
-
-interface IHdmiCecCallback {
-    /**
-     * The callback function that must be called by HAL implementation to notify
-     * the system of new CEC message arrival.
-     */
-    oneway onCecMessage(CecMessage message);
-
-    /**
-     * The callback function that must be called by HAL implementation to notify
-     * the system of new hotplug event.
-     */
-    oneway onHotplugEvent(HotplugEvent event);
-
-    /**
-     * The callback function must be called by HAL implementation to notify the
-     * system whenever CEC device information of CEC network change.
-     * HAL shall be ready for readDeviceInfo call before invoke this callback.
-     * This event is triggered by topology change of whole CEC network. It's
-     * different from HotplugEvent which is triggered between devices which are
-     * connected directly through HDMI cable.
-     */
-    oneway onTopologyEvent(CecTopologyEvent event);
-};
diff --git a/tv/cec/2.0/default/Android.bp b/tv/cec/2.0/default/Android.bp
deleted file mode 100644
index d3d5342..0000000
--- a/tv/cec/2.0/default/Android.bp
+++ /dev/null
@@ -1,40 +0,0 @@
-cc_library_shared {
-    name: "android.hardware.tv.cec@2.0-impl",
-    defaults: ["hidl_defaults"],
-    vendor: true,
-    relative_install_path: "hw",
-    srcs: ["HdmiCec.cpp"],
-
-    shared_libs: [
-        "libhidlbase",
-        "liblog",
-        "libbase",
-        "libutils",
-        "libhardware",
-        "android.hardware.tv.cec@2.0",
-    ],
-
-}
-
-cc_binary {
-    name: "android.hardware.tv.cec@2.0-service",
-    vintf_fragments: ["android.hardware.tv.cec@2.0-service.xml"],
-    defaults: ["hidl_defaults"],
-    relative_install_path: "hw",
-    vendor: true,
-    init_rc: ["android.hardware.tv.cec@2.0-service.rc"],
-    srcs: ["service.cpp"],
-
-    shared_libs: [
-        "liblog",
-        "libcutils",
-        "libdl",
-        "libbase",
-        "libutils",
-        "libhardware_legacy",
-        "libhardware",
-        "libhidlbase",
-        "android.hardware.tv.cec@2.0",
-    ],
-
-}
diff --git a/tv/cec/2.0/default/HdmiCec.cpp b/tv/cec/2.0/default/HdmiCec.cpp
deleted file mode 100644
index f451719..0000000
--- a/tv/cec/2.0/default/HdmiCec.cpp
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#define LOG_TAG "android.hardware.tv.cec@2.0-impl"
-#include <android-base/logging.h>
-
-#include <hardware/hardware.h>
-#include <hardware/hdmi_cec.h>
-#include "HdmiCec.h"
-
-namespace android {
-namespace hardware {
-namespace tv {
-namespace cec {
-namespace V2_0 {
-namespace implementation {
-
-static_assert(CEC_DEVICE_INACTIVE == static_cast<int>(CecDeviceType::INACTIVE),
-              "CecDeviceType::INACTIVE must match legacy value.");
-static_assert(CEC_DEVICE_TV == static_cast<int>(CecDeviceType::TV),
-              "CecDeviceType::TV must match legacy value.");
-static_assert(CEC_DEVICE_RECORDER == static_cast<int>(CecDeviceType::RECORDER),
-              "CecDeviceType::RECORDER must match legacy value.");
-static_assert(CEC_DEVICE_TUNER == static_cast<int>(CecDeviceType::TUNER),
-              "CecDeviceType::TUNER must match legacy value.");
-static_assert(CEC_DEVICE_PLAYBACK == static_cast<int>(CecDeviceType::PLAYBACK),
-              "CecDeviceType::PLAYBACK must match legacy value.");
-static_assert(CEC_DEVICE_AUDIO_SYSTEM == static_cast<int>(CecDeviceType::AUDIO_SYSTEM),
-              "CecDeviceType::AUDIO_SYSTEM must match legacy value.");
-/* TODO: Adjust for cec@2.0
-static_assert(CEC_DEVICE_MAX == static_cast<int>(CecDeviceType::MAX),
-        "CecDeviceType::MAX must match legacy value.");
-*/
-static_assert(CEC_ADDR_TV == static_cast<int>(CecLogicalAddress::TV),
-              "CecLogicalAddress::TV must match legacy value.");
-static_assert(CEC_ADDR_RECORDER_1 == static_cast<int>(CecLogicalAddress::RECORDER_1),
-              "CecLogicalAddress::RECORDER_1 must match legacy value.");
-static_assert(CEC_ADDR_RECORDER_2 == static_cast<int>(CecLogicalAddress::RECORDER_2),
-              "CecLogicalAddress::RECORDER_2 must match legacy value.");
-static_assert(CEC_ADDR_TUNER_1 == static_cast<int>(CecLogicalAddress::TUNER_1),
-              "CecLogicalAddress::TUNER_1 must match legacy value.");
-static_assert(CEC_ADDR_PLAYBACK_1 == static_cast<int>(CecLogicalAddress::PLAYBACK_1),
-              "CecLogicalAddress::PLAYBACK_1 must match legacy value.");
-static_assert(CEC_ADDR_AUDIO_SYSTEM == static_cast<int>(CecLogicalAddress::AUDIO_SYSTEM),
-              "CecLogicalAddress::AUDIO_SYSTEM must match legacy value.");
-static_assert(CEC_ADDR_TUNER_2 == static_cast<int>(CecLogicalAddress::TUNER_2),
-              "CecLogicalAddress::TUNER_2 must match legacy value.");
-static_assert(CEC_ADDR_TUNER_3 == static_cast<int>(CecLogicalAddress::TUNER_3),
-              "CecLogicalAddress::TUNER_3 must match legacy value.");
-static_assert(CEC_ADDR_PLAYBACK_2 == static_cast<int>(CecLogicalAddress::PLAYBACK_2),
-              "CecLogicalAddress::PLAYBACK_2 must match legacy value.");
-static_assert(CEC_ADDR_RECORDER_3 == static_cast<int>(CecLogicalAddress::RECORDER_3),
-              "CecLogicalAddress::RECORDER_3 must match legacy value.");
-static_assert(CEC_ADDR_TUNER_4 == static_cast<int>(CecLogicalAddress::TUNER_4),
-              "CecLogicalAddress::TUNER_4 must match legacy value.");
-static_assert(CEC_ADDR_PLAYBACK_3 == static_cast<int>(CecLogicalAddress::PLAYBACK_3),
-              "CecLogicalAddress::PLAYBACK_3 must match legacy value.");
-/* TODO: Adjust for cec@2.0
-static_assert(CEC_ADDR_FREE_USE == static_cast<int>(CecLogicalAddress::FREE_USE),
-        "CecLogicalAddress::FREE_USE must match legacy value.");
-*/
-static_assert(CEC_ADDR_UNREGISTERED == static_cast<int>(CecLogicalAddress::UNREGISTERED),
-              "CecLogicalAddress::UNREGISTERED must match legacy value.");
-static_assert(CEC_ADDR_BROADCAST == static_cast<int>(CecLogicalAddress::BROADCAST),
-              "CecLogicalAddress::BROADCAST must match legacy value.");
-
-static_assert(CEC_MESSAGE_FEATURE_ABORT == static_cast<int>(CecMessageType::FEATURE_ABORT),
-              "CecMessageType::FEATURE_ABORT must match legacy value.");
-static_assert(CEC_MESSAGE_IMAGE_VIEW_ON == static_cast<int>(CecMessageType::IMAGE_VIEW_ON),
-              "CecMessageType::IMAGE_VIEW_ON must match legacy value.");
-static_assert(CEC_MESSAGE_TUNER_STEP_INCREMENT ==
-                      static_cast<int>(CecMessageType::TUNER_STEP_INCREMENT),
-              "CecMessageType::TUNER_STEP_INCREMENT must match legacy value.");
-static_assert(CEC_MESSAGE_TUNER_STEP_DECREMENT ==
-                      static_cast<int>(CecMessageType::TUNER_STEP_DECREMENT),
-              "CecMessageType::TUNER_STEP_DECREMENT must match legacy value.");
-static_assert(CEC_MESSAGE_TUNER_DEVICE_STATUS ==
-                      static_cast<int>(CecMessageType::TUNER_DEVICE_STATUS),
-              "CecMessageType::TUNER_DEVICE_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_TUNER_DEVICE_STATUS ==
-                      static_cast<int>(CecMessageType::GIVE_TUNER_DEVICE_STATUS),
-              "CecMessageType::GIVE_TUNER_DEVICE_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_RECORD_ON == static_cast<int>(CecMessageType::RECORD_ON),
-              "CecMessageType::RECORD_ON must match legacy value.");
-static_assert(CEC_MESSAGE_RECORD_STATUS == static_cast<int>(CecMessageType::RECORD_STATUS),
-              "CecMessageType::RECORD_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_RECORD_OFF == static_cast<int>(CecMessageType::RECORD_OFF),
-              "CecMessageType::RECORD_OFF must match legacy value.");
-static_assert(CEC_MESSAGE_TEXT_VIEW_ON == static_cast<int>(CecMessageType::TEXT_VIEW_ON),
-              "CecMessageType::TEXT_VIEW_ON must match legacy value.");
-static_assert(CEC_MESSAGE_RECORD_TV_SCREEN == static_cast<int>(CecMessageType::RECORD_TV_SCREEN),
-              "CecMessageType::RECORD_TV_SCREEN must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_DECK_STATUS == static_cast<int>(CecMessageType::GIVE_DECK_STATUS),
-              "CecMessageType::GIVE_DECK_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_STANDBY == static_cast<int>(CecMessageType::STANDBY),
-              "CecMessageType::STANDBY must match legacy value.");
-static_assert(CEC_MESSAGE_PLAY == static_cast<int>(CecMessageType::PLAY),
-              "CecMessageType::PLAY must match legacy value.");
-static_assert(CEC_MESSAGE_DECK_CONTROL == static_cast<int>(CecMessageType::DECK_CONTROL),
-              "CecMessageType::DECK_CONTROL must match legacy value.");
-static_assert(CEC_MESSAGE_TIMER_CLEARED_STATUS ==
-                      static_cast<int>(CecMessageType::TIMER_CLEARED_STATUS),
-              "CecMessageType::TIMER_CLEARED_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_USER_CONTROL_PRESSED ==
-                      static_cast<int>(CecMessageType::USER_CONTROL_PRESSED),
-              "CecMessageType::USER_CONTROL_PRESSED must match legacy value.");
-static_assert(CEC_MESSAGE_USER_CONTROL_RELEASED ==
-                      static_cast<int>(CecMessageType::USER_CONTROL_RELEASED),
-              "CecMessageType::USER_CONTROL_RELEASED must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_OSD_NAME == static_cast<int>(CecMessageType::GIVE_OSD_NAME),
-              "CecMessageType::GIVE_OSD_NAME must match legacy value.");
-static_assert(CEC_MESSAGE_SET_OSD_NAME == static_cast<int>(CecMessageType::SET_OSD_NAME),
-              "CecMessageType::SET_OSD_NAME must match legacy value.");
-static_assert(CEC_MESSAGE_SYSTEM_AUDIO_MODE_REQUEST ==
-                      static_cast<int>(CecMessageType::SYSTEM_AUDIO_MODE_REQUEST),
-              "CecMessageType::SYSTEM_AUDIO_MODE_REQUEST must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_AUDIO_STATUS == static_cast<int>(CecMessageType::GIVE_AUDIO_STATUS),
-              "CecMessageType::GIVE_AUDIO_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_SET_SYSTEM_AUDIO_MODE ==
-                      static_cast<int>(CecMessageType::SET_SYSTEM_AUDIO_MODE),
-              "CecMessageType::SET_SYSTEM_AUDIO_MODE must match legacy value.");
-static_assert(CEC_MESSAGE_REPORT_AUDIO_STATUS ==
-                      static_cast<int>(CecMessageType::REPORT_AUDIO_STATUS),
-              "CecMessageType::REPORT_AUDIO_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_SYSTEM_AUDIO_MODE_STATUS ==
-                      static_cast<int>(CecMessageType::GIVE_SYSTEM_AUDIO_MODE_STATUS),
-              "CecMessageType::GIVE_SYSTEM_AUDIO_MODE_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_SYSTEM_AUDIO_MODE_STATUS ==
-                      static_cast<int>(CecMessageType::SYSTEM_AUDIO_MODE_STATUS),
-              "CecMessageType::SYSTEM_AUDIO_MODE_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_ROUTING_CHANGE == static_cast<int>(CecMessageType::ROUTING_CHANGE),
-              "CecMessageType::ROUTING_CHANGE must match legacy value.");
-static_assert(CEC_MESSAGE_ROUTING_INFORMATION ==
-                      static_cast<int>(CecMessageType::ROUTING_INFORMATION),
-              "CecMessageType::ROUTING_INFORMATION must match legacy value.");
-static_assert(CEC_MESSAGE_ACTIVE_SOURCE == static_cast<int>(CecMessageType::ACTIVE_SOURCE),
-              "CecMessageType::ACTIVE_SOURCE must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_PHYSICAL_ADDRESS ==
-                      static_cast<int>(CecMessageType::GIVE_PHYSICAL_ADDRESS),
-              "CecMessageType::GIVE_PHYSICAL_ADDRESS must match legacy value.");
-static_assert(CEC_MESSAGE_REPORT_PHYSICAL_ADDRESS ==
-                      static_cast<int>(CecMessageType::REPORT_PHYSICAL_ADDRESS),
-              "CecMessageType::REPORT_PHYSICAL_ADDRESS must match legacy value.");
-static_assert(CEC_MESSAGE_REQUEST_ACTIVE_SOURCE ==
-                      static_cast<int>(CecMessageType::REQUEST_ACTIVE_SOURCE),
-              "CecMessageType::REQUEST_ACTIVE_SOURCE must match legacy value.");
-static_assert(CEC_MESSAGE_SET_STREAM_PATH == static_cast<int>(CecMessageType::SET_STREAM_PATH),
-              "CecMessageType::SET_STREAM_PATH must match legacy value.");
-static_assert(CEC_MESSAGE_DEVICE_VENDOR_ID == static_cast<int>(CecMessageType::DEVICE_VENDOR_ID),
-              "CecMessageType::DEVICE_VENDOR_ID must match legacy value.");
-static_assert(CEC_MESSAGE_VENDOR_COMMAND == static_cast<int>(CecMessageType::VENDOR_COMMAND),
-              "CecMessageType::VENDOR_COMMAND must match legacy value.");
-static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_DOWN ==
-                      static_cast<int>(CecMessageType::VENDOR_REMOTE_BUTTON_DOWN),
-              "CecMessageType::VENDOR_REMOTE_BUTTON_DOWN must match legacy value.");
-static_assert(CEC_MESSAGE_VENDOR_REMOTE_BUTTON_UP ==
-                      static_cast<int>(CecMessageType::VENDOR_REMOTE_BUTTON_UP),
-              "CecMessageType::VENDOR_REMOTE_BUTTON_UP must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_DEVICE_VENDOR_ID ==
-                      static_cast<int>(CecMessageType::GIVE_DEVICE_VENDOR_ID),
-              "CecMessageType::GIVE_DEVICE_VENDOR_ID must match legacy value.");
-static_assert(CEC_MESSAGE_MENU_REQUEST == static_cast<int>(CecMessageType::MENU_REQUEST),
-              "CecMessageType::MENU_REQUEST must match legacy value.");
-static_assert(CEC_MESSAGE_MENU_STATUS == static_cast<int>(CecMessageType::MENU_STATUS),
-              "CecMessageType::MENU_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_GIVE_DEVICE_POWER_STATUS ==
-                      static_cast<int>(CecMessageType::GIVE_DEVICE_POWER_STATUS),
-              "CecMessageType::GIVE_DEVICE_POWER_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_REPORT_POWER_STATUS ==
-                      static_cast<int>(CecMessageType::REPORT_POWER_STATUS),
-              "CecMessageType::REPORT_POWER_STATUS must match legacy value.");
-static_assert(CEC_MESSAGE_GET_MENU_LANGUAGE == static_cast<int>(CecMessageType::GET_MENU_LANGUAGE),
-              "CecMessageType::GET_MENU_LANGUAGE must match legacy value.");
-static_assert(CEC_MESSAGE_SELECT_ANALOG_SERVICE ==
-                      static_cast<int>(CecMessageType::SELECT_ANALOG_SERVICE),
-              "CecMessageType::SELECT_ANALOG_SERVICE must match legacy value.");
-static_assert(CEC_MESSAGE_SELECT_DIGITAL_SERVICE ==
-                      static_cast<int>(CecMessageType::SELECT_DIGITAL_SERVICE),
-              "CecMessageType::SELECT_DIGITAL_SERVICE must match legacy value.");
-static_assert(CEC_MESSAGE_SET_DIGITAL_TIMER == static_cast<int>(CecMessageType::SET_DIGITAL_TIMER),
-              "CecMessageType::SET_DIGITAL_TIMER must match legacy value.");
-static_assert(CEC_MESSAGE_CLEAR_DIGITAL_TIMER ==
-                      static_cast<int>(CecMessageType::CLEAR_DIGITAL_TIMER),
-              "CecMessageType::CLEAR_DIGITAL_TIMER must match legacy value.");
-static_assert(CEC_MESSAGE_SET_AUDIO_RATE == static_cast<int>(CecMessageType::SET_AUDIO_RATE),
-              "CecMessageType::SET_AUDIO_RATE must match legacy value.");
-static_assert(CEC_MESSAGE_INACTIVE_SOURCE == static_cast<int>(CecMessageType::INACTIVE_SOURCE),
-              "CecMessageType::INACTIVE_SOURCE must match legacy value.");
-static_assert(CEC_MESSAGE_CEC_VERSION == static_cast<int>(CecMessageType::CEC_VERSION),
-              "CecMessageType::CEC_VERSION must match legacy value.");
-static_assert(CEC_MESSAGE_GET_CEC_VERSION == static_cast<int>(CecMessageType::GET_CEC_VERSION),
-              "CecMessageType::GET_CEC_VERSION must match legacy value.");
-static_assert(CEC_MESSAGE_VENDOR_COMMAND_WITH_ID ==
-                      static_cast<int>(CecMessageType::VENDOR_COMMAND_WITH_ID),
-              "CecMessageType::VENDOR_COMMAND_WITH_ID must match legacy value.");
-static_assert(CEC_MESSAGE_CLEAR_EXTERNAL_TIMER ==
-                      static_cast<int>(CecMessageType::CLEAR_EXTERNAL_TIMER),
-              "CecMessageType::CLEAR_EXTERNAL_TIMER must match legacy value.");
-static_assert(CEC_MESSAGE_SET_EXTERNAL_TIMER ==
-                      static_cast<int>(CecMessageType::SET_EXTERNAL_TIMER),
-              "CecMessageType::SET_EXTERNAL_TIMER must match legacy value.");
-static_assert(CEC_MESSAGE_INITIATE_ARC == static_cast<int>(CecMessageType::INITIATE_ARC),
-              "CecMessageType::INITIATE_ARC must match legacy value.");
-static_assert(CEC_MESSAGE_REPORT_ARC_INITIATED ==
-                      static_cast<int>(CecMessageType::REPORT_ARC_INITIATED),
-              "CecMessageType::REPORT_ARC_INITIATED must match legacy value.");
-static_assert(CEC_MESSAGE_REPORT_ARC_TERMINATED ==
-                      static_cast<int>(CecMessageType::REPORT_ARC_TERMINATED),
-              "CecMessageType::REPORT_ARC_TERMINATED must match legacy value.");
-static_assert(CEC_MESSAGE_REQUEST_ARC_INITIATION ==
-                      static_cast<int>(CecMessageType::REQUEST_ARC_INITIATION),
-              "CecMessageType::REQUEST_ARC_INITIATION must match legacy value.");
-static_assert(CEC_MESSAGE_REQUEST_ARC_TERMINATION ==
-                      static_cast<int>(CecMessageType::REQUEST_ARC_TERMINATION),
-              "CecMessageType::REQUEST_ARC_TERMINATION must match legacy value.");
-static_assert(CEC_MESSAGE_TERMINATE_ARC == static_cast<int>(CecMessageType::TERMINATE_ARC),
-              "CecMessageType::TERMINATE_ARC must match legacy value.");
-static_assert(CEC_MESSAGE_ABORT == static_cast<int>(CecMessageType::ABORT),
-              "CecMessageType::ABORT must match legacy value.");
-
-static_assert(ABORT_UNRECOGNIZED_MODE == static_cast<int>(AbortReason::UNRECOGNIZED_MODE),
-              "AbortReason::UNRECOGNIZED_MODE must match legacy value.");
-static_assert(ABORT_NOT_IN_CORRECT_MODE == static_cast<int>(AbortReason::NOT_IN_CORRECT_MODE),
-              "AbortReason::NOT_IN_CORRECT_MODE must match legacy value.");
-static_assert(ABORT_CANNOT_PROVIDE_SOURCE == static_cast<int>(AbortReason::CANNOT_PROVIDE_SOURCE),
-              "AbortReason::CANNOT_PROVIDE_SOURCE must match legacy value.");
-static_assert(ABORT_INVALID_OPERAND == static_cast<int>(AbortReason::INVALID_OPERAND),
-              "AbortReason::INVALID_OPERAND must match legacy value.");
-static_assert(ABORT_REFUSED == static_cast<int>(AbortReason::REFUSED),
-              "AbortReason::REFUSED must match legacy value.");
-static_assert(ABORT_UNABLE_TO_DETERMINE == static_cast<int>(AbortReason::UNABLE_TO_DETERMINE),
-              "AbortReason::UNABLE_TO_DETERMINE must match legacy value.");
-
-static_assert(HDMI_RESULT_SUCCESS == static_cast<int>(SendMessageResult::SUCCESS),
-              "SendMessageResult::SUCCESS must match legacy value.");
-static_assert(HDMI_RESULT_NACK == static_cast<int>(SendMessageResult::NACK),
-              "SendMessageResult::NACK must match legacy value.");
-static_assert(HDMI_RESULT_BUSY == static_cast<int>(SendMessageResult::BUSY),
-              "SendMessageResult::BUSY must match legacy value.");
-static_assert(HDMI_RESULT_FAIL == static_cast<int>(SendMessageResult::FAIL),
-              "SendMessageResult::FAIL must match legacy value.");
-
-static_assert(HDMI_INPUT == static_cast<int>(HdmiPortType::INPUT),
-              "HdmiPortType::INPUT must match legacy value.");
-static_assert(HDMI_OUTPUT == static_cast<int>(HdmiPortType::OUTPUT),
-              "HdmiPortType::OUTPUT must match legacy value.");
-
-static_assert(HDMI_OPTION_WAKEUP == static_cast<int>(OptionKey::WAKEUP),
-              "OptionKey::WAKEUP must match legacy value.");
-static_assert(HDMI_OPTION_ENABLE_CEC == static_cast<int>(OptionKey::ENABLE_CEC),
-              "OptionKey::ENABLE_CEC must match legacy value.");
-static_assert(HDMI_OPTION_SYSTEM_CEC_CONTROL == static_cast<int>(OptionKey::SYSTEM_CEC_CONTROL),
-              "OptionKey::SYSTEM_CEC_CONTROL must match legacy value.");
-
-sp<IHdmiCecCallback> HdmiCec::mCallback = nullptr;
-
-HdmiCec::HdmiCec(hdmi_cec_device_t* device) : mDevice(device) {}
-
-// Methods from ::android::hardware::tv::cec::V2_0::IHdmiCec follow.
-Return<Result> HdmiCec::addDeviceType(CecDeviceType deviceType) {
-    // TODO implement
-    if (deviceType <= CecDeviceType::MAX) {
-        return Result::SUCCESS;
-    } else {
-        return Result::FAILURE_INVALID_ARGS;
-    }
-}
-
-Return<void> HdmiCec::clearDeviceTypes() {
-    // TODO implement
-    return Void();
-}
-
-Return<void> HdmiCec::setAllDeviceTypes(CecAllDeviceTypes allDeviceTypes) {
-    // TODO implement
-    if (allDeviceTypes == 1) {
-    }
-    return Void();
-}
-
-Return<void> HdmiCec::setDeviceFeatures(CecDeviceType deviceType,
-                                        CecDeviceFeatures /* deviceFeatures */) {
-    // TODO implement
-    if (deviceType != CecDeviceType::MAX) {
-    }
-    return Void();
-}
-
-Return<void> HdmiCec::setRcProfile(CecDeviceType deviceType, const CecRcProfile& /* rcProfile */) {
-    // TODO implement
-    if (deviceType != CecDeviceType::MAX) {
-    }
-    return Void();
-}
-
-Return<void> HdmiCec::readDeviceInfo(CecLogicalAddress logicalAddress,
-                                     CecPhysicalAddress physicalAddress,
-                                     const readDeviceInfo_cb _hidl_cb) {
-    // TODO implement
-    CecDeviceInfo deviceInfo;
-
-    if (logicalAddress == CecLogicalAddress::TV) {
-        _hidl_cb(Result::SUCCESS, deviceInfo);
-        if (physicalAddress) {
-        }
-    }
-    return Void();
-}
-
-Return<SendMessageResult> HdmiCec::sendMessage(const CecMessage& message) {
-    cec_message_t legacyMessage{
-            .initiator = static_cast<cec_logical_address_t>(message.initiator),
-            .destination = static_cast<cec_logical_address_t>(message.destination),
-            .length = message.body.size(),
-    };
-    for (size_t i = 0; i < message.body.size(); ++i) {
-        legacyMessage.body[i] = static_cast<unsigned char>(message.body[i]);
-    }
-    return static_cast<SendMessageResult>(mDevice->send_message(mDevice, &legacyMessage));
-}
-
-Return<void> HdmiCec::setCallback(const sp<IHdmiCecCallback>& callback) {
-    if (mCallback != nullptr) {
-        mCallback->unlinkToDeath(this);
-        mCallback = nullptr;
-    }
-
-    if (callback != nullptr) {
-        mCallback = callback;
-        mCallback->linkToDeath(this, 0 /*cookie*/);
-        mDevice->register_event_callback(mDevice, eventCallback, nullptr);
-    }
-    return Void();
-}
-
-Return<void> HdmiCec::getPortInfo(getPortInfo_cb _hidl_cb) {
-    struct hdmi_port_info* legacyPorts;
-    int numPorts;
-    hidl_vec<HdmiPortInfo> portInfos;
-    mDevice->get_port_info(mDevice, &legacyPorts, &numPorts);
-    portInfos.resize(numPorts);
-    for (int i = 0; i < numPorts; ++i) {
-        portInfos[i] = {.type = static_cast<HdmiPortType>(legacyPorts[i].type),
-                        .portId = static_cast<HdmiPortId>(legacyPorts[i].port_id),
-                        .cecSupported = legacyPorts[i].cec_supported != 0,
-                        .arcSupported = legacyPorts[i].arc_supported != 0,
-                        .physicalAddress = legacyPorts[i].physical_address};
-    }
-    _hidl_cb(portInfos);
-    return Void();
-}
-
-Return<void> HdmiCec::setOption(OptionKey key, bool value) {
-    mDevice->set_option(mDevice, static_cast<int>(key), value ? 1 : 0);
-    return Void();
-}
-
-Return<void> HdmiCec::setLanguage(const hidl_string& language) {
-    if (language.size() != 3) {
-        LOG(ERROR) << "Wrong language code: expected 3 letters, but it was " << language.size()
-                   << ".";
-        return Void();
-    }
-    const char* languageStr = language.c_str();
-    int convertedLanguage = ((languageStr[0] & 0xFF) << 16) | ((languageStr[1] & 0xFF) << 8) |
-                            (languageStr[2] & 0xFF);
-    mDevice->set_option(mDevice, HDMI_OPTION_SET_LANG, convertedLanguage);
-    return Void();
-}
-
-Return<void> HdmiCec::enableAudioReturnChannel(HdmiPortId portId, bool enable) {
-    mDevice->set_audio_return_channel(mDevice, portId, enable ? 1 : 0);
-    return Void();
-}
-
-Return<bool> HdmiCec::isConnected(HdmiPortId portId) {
-    return mDevice->is_connected(mDevice, portId) > 0;
-}
-
-IHdmiCec* HIDL_FETCH_IHdmiCec(const char* hal) {
-    hdmi_cec_device_t* hdmi_cec_device;
-    int ret = 0;
-    const hw_module_t* hw_module = nullptr;
-
-    ret = hw_get_module(HDMI_CEC_HARDWARE_MODULE_ID, &hw_module);
-    if (ret == 0) {
-        ret = hdmi_cec_open(hw_module, &hdmi_cec_device);
-        if (ret != 0) {
-            LOG(ERROR) << "hdmi_cec_open " << hal << " failed: " << ret;
-        }
-    } else {
-        LOG(ERROR) << "hw_get_module " << hal << " failed: " << ret;
-    }
-
-    if (ret == 0) {
-        return new HdmiCec(hdmi_cec_device);
-    } else {
-        LOG(ERROR) << "Passthrough failed to load legacy HAL.";
-        return nullptr;
-    }
-}
-
-}  // namespace implementation
-}  // namespace V2_0
-}  // namespace cec
-}  // namespace tv
-}  // namespace hardware
-}  // namespace android
diff --git a/tv/cec/2.0/default/HdmiCec.h b/tv/cec/2.0/default/HdmiCec.h
deleted file mode 100644
index ab54770..0000000
--- a/tv/cec/2.0/default/HdmiCec.h
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * Copyright (C) 2019 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_TV_CEC_V2_0_HDMICEC_H
-#define ANDROID_HARDWARE_TV_CEC_V2_0_HDMICEC_H
-
-#include <algorithm>
-
-#include <android/hardware/tv/cec/2.0/IHdmiCec.h>
-#include <hardware/hardware.h>
-#include <hardware/hdmi_cec.h>
-#include <hidl/Status.h>
-
-#include <hidl/MQDescriptor.h>
-namespace android {
-namespace hardware {
-namespace tv {
-namespace cec {
-namespace V2_0 {
-namespace implementation {
-
-using ::android::sp;
-using ::android::hardware::hidl_string;
-using ::android::hardware::hidl_vec;
-using ::android::hardware::Return;
-using ::android::hardware::Void;
-using ::android::hardware::tv::cec::V2_0::CecLogicalAddress;
-using ::android::hardware::tv::cec::V2_0::CecMessage;
-using ::android::hardware::tv::cec::V2_0::CecPhysicalAddress;
-using ::android::hardware::tv::cec::V2_0::HdmiPortId;
-using ::android::hardware::tv::cec::V2_0::HdmiPortInfo;
-using ::android::hardware::tv::cec::V2_0::IHdmiCec;
-using ::android::hardware::tv::cec::V2_0::IHdmiCecCallback;
-using ::android::hardware::tv::cec::V2_0::MaxLength;
-using ::android::hardware::tv::cec::V2_0::OptionKey;
-using ::android::hardware::tv::cec::V2_0::Result;
-using ::android::hardware::tv::cec::V2_0::SendMessageResult;
-
-struct HdmiCec : public IHdmiCec, public hidl_death_recipient {
-    HdmiCec(hdmi_cec_device_t* device);
-    // Methods from ::android::hardware::tv::cec::V2_0::IHdmiCec follow.
-    Return<Result> addDeviceType(CecDeviceType deviceType) override;
-    Return<void> clearDeviceTypes() override;
-    Return<void> setAllDeviceTypes(CecAllDeviceTypes allDeviceTypes) override;
-    Return<void> setDeviceFeatures(CecDeviceType deviceType,
-                                   CecDeviceFeatures /* deviceFeatures */) override;
-    Return<void> setRcProfile(CecDeviceType deviceType,
-                              const CecRcProfile& /* rcProfile */) override;
-    Return<void> readDeviceInfo(CecLogicalAddress logicalAddress,
-                                CecPhysicalAddress physicalAddress,
-                                const readDeviceInfo_cb _hidl_cb) override;
-    Return<SendMessageResult> sendMessage(const CecMessage& message) override;
-    Return<void> setCallback(const sp<IHdmiCecCallback>& callback) override;
-    Return<void> getPortInfo(getPortInfo_cb _hidl_cb) override;
-    Return<void> setOption(OptionKey key, bool value) override;
-    Return<void> setLanguage(const hidl_string& language) override;
-    Return<void> enableAudioReturnChannel(HdmiPortId portId, bool enable) override;
-    Return<bool> isConnected(HdmiPortId portId) override;
-
-    static void eventCallback(const hdmi_event_t* event, void* /* arg */) {
-        if (mCallback != nullptr && event != nullptr) {
-            if (event->type == HDMI_EVENT_CEC_MESSAGE) {
-                size_t length =
-                        std::min(event->cec.length, static_cast<size_t>(MaxLength::MESSAGE_BODY));
-                CecMessage cecMessage{
-                        .initiator = static_cast<CecLogicalAddress>(event->cec.initiator),
-                        .destination = static_cast<CecLogicalAddress>(event->cec.destination),
-                };
-                cecMessage.body.resize(length);
-                for (size_t i = 0; i < length; ++i) {
-                    cecMessage.body[i] = static_cast<uint8_t>(event->cec.body[i]);
-                }
-                mCallback->onCecMessage(cecMessage);
-            } else if (event->type == HDMI_EVENT_HOT_PLUG) {
-                HotplugEvent hotplugEvent{
-                        .connected = event->hotplug.connected > 0,
-                        .portId = static_cast<HdmiPortId>(event->hotplug.port_id)};
-                mCallback->onHotplugEvent(hotplugEvent);
-            }
-        }
-    }
-
-    virtual void serviceDied(uint64_t /*cookie*/,
-                             const wp<::android::hidl::base::V1_0::IBase>& /*who*/) {
-        setCallback(nullptr);
-    }
-
-   private:
-    static sp<IHdmiCecCallback> mCallback;
-    const hdmi_cec_device_t* mDevice;
-};
-
-extern "C" IHdmiCec* HIDL_FETCH_IHdmiCec(const char* name);
-
-}  // namespace implementation
-}  // namespace V2_0
-}  // namespace cec
-}  // namespace tv
-}  // namespace hardware
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_TV_CEC_V2_0_HDMICEC_H
diff --git a/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.rc b/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.rc
deleted file mode 100644
index 1e8cd80..0000000
--- a/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.rc
+++ /dev/null
@@ -1,4 +0,0 @@
-service vendor.cec-hal-2-0 /vendor/bin/hw/android.hardware.tv.cec@2.0-service
-    class hal
-    user system
-    group system
diff --git a/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.xml b/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.xml
deleted file mode 100644
index 61fb1bb..0000000
--- a/tv/cec/2.0/default/android.hardware.tv.cec@2.0-service.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-<manifest version="1.0" type="device">
-    <hal format="hidl">
-        <name>android.hardware.tv.cec</name>
-        <transport>hwbinder</transport>
-        <version>2.0</version>
-        <interface>
-            <name>IHdmiCec</name>
-            <instance>default</instance>
-        </interface>
-    </hal>
-</manifest>
diff --git a/tv/cec/2.0/default/service.cpp b/tv/cec/2.0/default/service.cpp
deleted file mode 100644
index dacc38c..0000000
--- a/tv/cec/2.0/default/service.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-#define LOG_TAG "android.hardware.tv.cec@2.0-service"
-
-#include <android/hardware/tv/cec/2.0/IHdmiCec.h>
-#include <hidl/LegacySupport.h>
-
-using android::hardware::defaultPassthroughServiceImplementation;
-using android::hardware::tv::cec::V2_0::IHdmiCec;
-
-int main() {
-    return defaultPassthroughServiceImplementation<IHdmiCec>();
-}
diff --git a/tv/cec/2.0/types.hal b/tv/cec/2.0/types.hal
deleted file mode 100644
index cad6c39..0000000
--- a/tv/cec/2.0/types.hal
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * Copyright (C) 2019 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.
- */
-
-package android.hardware.tv.cec@2.0;
-
-import android.hidl.safe_union@1.0;
-
-/**
- * CEC device type as specified in CEC Table 11-7 of the CEC spec 2.0b.
- */
-enum CecDeviceType : int32_t {
-    INACTIVE = -1,
-    TV = 0,
-    RECORDER = 1,
-    TUNER = 3,
-    PLAYBACK = 4,
-    AUDIO_SYSTEM = 5,
-    PURE_CEC_SWITCH = 6,
-    PROCESSOR = 7,
-    MAX = PROCESSOR,
-};
-
-/**
- * CEC logical address as specified in CEC Table 11-9 of the CEC spec 2.0b.
- */
-enum CecLogicalAddress : int32_t {
-    TV = 0,
-    RECORDER_1 = 1,
-    RECORDER_2 = 2,
-    TUNER_1 = 3,
-    PLAYBACK_1 = 4,
-    AUDIO_SYSTEM = 5,
-    TUNER_2 = 6,
-    TUNER_3 = 7,
-    PLAYBACK_2 = 8,
-    RECORDER_3 = 9,
-    TUNER_4 = 10,
-    PLAYBACK_3 = 11,
-    BACKUP_1 = 12, // backup1 for Playback/Recording/Tuner/Processor device
-    BACKUP_2 = 13, // backup2 for Playback/Recording/Tuner/Processor device
-    SPECIFIC_USE = 14,
-    UNREGISTERED = 15, // as Initiator address
-    BROADCAST = 15, // as Destination address
-};
-
-/**
- * HDMI CEC message types.
- *
- * The assigned values represent opcode used in CEC frame as specified in
- * Section 11.10 of the CEC spec 2.0b on top of Section CEC 15 of the CEC
- * Spec 1.4b.
- */
-enum CecMessageType : int32_t {
-    FEATURE_ABORT = 0x00,
-    IMAGE_VIEW_ON = 0x04,
-    TUNER_STEP_INCREMENT = 0x05,
-    TUNER_STEP_DECREMENT = 0x06,
-    TUNER_DEVICE_STATUS = 0x07,
-    GIVE_TUNER_DEVICE_STATUS = 0x08,
-    RECORD_ON = 0x09,
-    RECORD_STATUS = 0x0A,
-    RECORD_OFF = 0x0B,
-    TEXT_VIEW_ON = 0x0D,
-    RECORD_TV_SCREEN = 0x0F,
-    GIVE_DECK_STATUS = 0x1A,
-    DECK_STATUS = 0x1B,
-    SET_MENU_LANGUAGE = 0x32,
-    CLEAR_ANALOG_TIMER = 0x33,
-    SET_ANALOG_TIMER = 0x34,
-    TIMER_STATUS = 0x35,
-    STANDBY = 0x36,
-    PLAY = 0x41,
-    DECK_CONTROL = 0x42,
-    TIMER_CLEARED_STATUS = 0x43,
-    USER_CONTROL_PRESSED = 0x44,
-    USER_CONTROL_RELEASED = 0x45,
-    GIVE_OSD_NAME = 0x46,
-    SET_OSD_NAME = 0x47,
-    SET_OSD_STRING = 0x64,
-    SET_TIMER_PROGRAM_TITLE = 0x67,
-    SYSTEM_AUDIO_MODE_REQUEST = 0x70,
-    GIVE_AUDIO_STATUS = 0x71,
-    SET_SYSTEM_AUDIO_MODE = 0x72,
-    REPORT_AUDIO_STATUS = 0x7A,
-    GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
-    SYSTEM_AUDIO_MODE_STATUS = 0x7E,
-    ROUTING_CHANGE = 0x80,
-    ROUTING_INFORMATION = 0x81,
-    ACTIVE_SOURCE = 0x82,
-    GIVE_PHYSICAL_ADDRESS = 0x83,
-    REPORT_PHYSICAL_ADDRESS = 0x84,
-    REQUEST_ACTIVE_SOURCE = 0x85,
-    SET_STREAM_PATH = 0x86,
-    DEVICE_VENDOR_ID = 0x87,
-    VENDOR_COMMAND = 0x89,
-    VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
-    VENDOR_REMOTE_BUTTON_UP = 0x8B,
-    GIVE_DEVICE_VENDOR_ID = 0x8C,
-    MENU_REQUEST = 0x8D,
-    MENU_STATUS = 0x8E,
-    GIVE_DEVICE_POWER_STATUS = 0x8F,
-    REPORT_POWER_STATUS = 0x90,
-    GET_MENU_LANGUAGE = 0x91,
-    SELECT_ANALOG_SERVICE = 0x92,
-    SELECT_DIGITAL_SERVICE = 0x93,
-    SET_DIGITAL_TIMER = 0x97,
-    CLEAR_DIGITAL_TIMER = 0x99,
-    SET_AUDIO_RATE = 0x9A,
-    INACTIVE_SOURCE = 0x9D,
-    CEC_VERSION = 0x9E,
-    GET_CEC_VERSION = 0x9F,
-    VENDOR_COMMAND_WITH_ID = 0xA0,
-    CLEAR_EXTERNAL_TIMER = 0xA1,
-    SET_EXTERNAL_TIMER = 0xA2,
-    REPORT_SHORT_AUDIO_DESCRIPTOR = 0xA3,
-    REQUEST_SHORT_AUDIO_DESCRIPTOR = 0xA4,
-    GIVE_FEATURES = 0XA5,
-    REPORT_FEATURES = 0xA6,
-    REQUEST_CURRENT_LATENCY = 0xA7,
-    REPORT_CURRENT_LATENCY = 0xA8,
-    INITIATE_ARC = 0xC0,
-    REPORT_ARC_INITIATED = 0xC1,
-    REPORT_ARC_TERMINATED = 0xC2,
-    REQUEST_ARC_INITIATION = 0xC3,
-    REQUEST_ARC_TERMINATION = 0xC4,
-    TERMINATE_ARC = 0xC5,
-    ABORT = 0xFF,
-    POLLING_MESSAGE = 0xFFFFFF00, // used for cec polling message
-};
-
-/**
- * Abort Reason as specified in CEC Table 29 of the CEC spec 1.4b.
- */
-enum AbortReason : int32_t {
-    UNRECOGNIZED_MODE = 0,
-    NOT_IN_CORRECT_MODE = 1,
-    CANNOT_PROVIDE_SOURCE = 2,
-    INVALID_OPERAND = 3,
-    REFUSED = 4,
-    UNABLE_TO_DETERMINE = 5,
-};
-
-enum MaxLength : int32_t {
-    MESSAGE_BODY = 14,
-};
-
-struct CecMessage {
-    /** logical address of sender */
-    CecLogicalAddress initiator;
-
-    /** logical address of receiver */
-    CecLogicalAddress destination;
-
-    /** cec message type */
-    CecMessageType cecMessageType;
-
-    /**
-     * The maximum size of body is 14 (MaxLength::MESSAGE_BODY) as specified in
-     * the section 6 of the CEC Spec 1.4b. Overflowed data must be ignored.
-     */
-    vec<uint8_t> body;
-};
-
-/**
- * error code used for send_message.
- */
-enum SendMessageResult : int32_t {
-    SUCCESS = 0,
-    NACK = 1, // not acknowledged
-    BUSY = 2, // bus is busy
-    FAIL = 3,
-};
-
-/**
- * CEC All Device Type Value as specified in Table 11-30 of the CEC spec 2.0b.
- */
-enum CecAllDeviceTypeValue : uint8_t {
-    RESERVED_DEVICE_2 = 1 << 0,
-    RESERVED_DEVICE_1 = 1 << 1,
-    CEC_SWITCH_DEVICE = 1 << 2,
-    AUDIO_DEVICE = 1 << 3,
-    PLAYBACK_DEVICE = 1 << 4,
-    TUNER_DEVICE = 1 << 5,
-    RECORDING_DEVICE = 1 << 6,
-    TV_DEVICE   = 1 << 7,
-};
-
-/**
- * CEC All Device Types
- *
- * It is a combination of all supported type from CecAllDeviceTypeValue.
- * For example a record with tuner functionalitye,
- * cecAllDeviceTypes = ((CecAllDeviceTypeValue::RECORDING_DEVICE)
- *                     |(CecAllDeviceTypeValue::TUNER_DEVICE))
- */
-typedef bitfield<CecAllDeviceTypeValue> CecAllDeviceTypes;
-
-/**
- * CEC Versions as specified in CEC Table 11-30 of the CEC spec 2.0b.
- */
-enum CecVersion : int32_t {
-    V_1_3_A   = 0x04,
-    V_1_4   = 0x05, // indicate CEC 1.4, 1.4a or 1.4b
-    V_2_0   = 0x06,
-};
-
-/**
- * Device Feature
- *
- * It is specified in CEC Table 11-30 of the CEC spec 2.0b. As a uint32 there
- * is room for future extensions aka DeviceFeature2 through DeviceFeature4.
- */
-enum CecDeviceFeature : uint32_t {
-    RESERVED = 1 << 0,
-    SOURCE_SUPPORT_ARC_RX = 1 << 1,
-    SINK_SUPPORT_ARC_TX = 1 << 2,
-    SOURCE_SUPPORT_SET_AUDIO_RATE = 1 << 3,
-    SUPPORT_CONTROLLED_BY_DECK = 1 << 4,
-    TV_SUPPORT_SET_OSD_STRINGS = 1 << 5,
-    TV_SUPPORT_RECORD_TV_SCREEN = 1 << 6,
-};
-
-/**
- * CEC Device Features
- *
- * It is a combination of all supported features from CecDeviceFeature.
- * For example a TV with OSD and ARC capabilities,
- *   CecDeviceFeatures = ((CecDeviceFeature::TV_SUPPORT_SET_OSD_STRINGS)
- *                       |(CecDeviceFeature::SINK_SUPPORT_ARC_TX))
- */
-typedef bitfield<CecDeviceFeature> CecDeviceFeatures;
-
-/**
- * Remote Control Profile
- *
- * It is specified in CEC Table 11-30 of the CEC spec 2.0b.
- */
-enum CecRcProfileId : uint8_t {
-    NONE = 0,   // TV doesn’t support any of these profiles
-    RC_PROFILE_1 = 0x02, // minimalistic zapper (low button count)
-    RC_PROFILE_2 = 0x06, // intermediate between profile 1 and profile 3
-    RC_PROFILE_3 = 0x0A, // typical TV remote
-    RC_PROFILE_4 = 0x0E, // extended form of profile 3
-};
-
-/**
- * Remote Control Profile Source
- *
- * It is specified in CEC Table 11-30 of the CEC spec 2.0b.
- */
-enum CecRcProfileSource : uint8_t {
-    MEDIA_CONTEXT_SENSITIVE = 1 << 0, // source can handle UI command 0x11
-    MEDIA_TO = 1 << 1, // source can handle UI command 0x10
-    CONTENTS = 1 << 2, // source can handle UI command 0x0B
-    DEVICE_SETUP = 1 << 3, // source can handle UI command 0x0A
-    DEVICE_ROOT = 1 << 4, // source can handle UI command 0x09
-    SOURCE_FLAG = 1 << 6, // Indicate the profile is for source
-};
-
-/**
- * Remote Control Profile for either TV or Source.
- */
-safe_union CecRcProfile1 {
-    /** CEC remote control profile for TV. */
-    CecRcProfileId profileId;
-
-    /* CEC remote control profile for source
-     *
-     * It is a combination of all supported profiles from CecRcProfileSource.
-     * For example a playback device support root menu and setup menu,
-     * profileSource = ((CecRcProfileSource::DEVICE_ROOT)
-     *                  |(CecRcProfileSource::DEVICE_SETUP)
-     *                  |(CecRcProfileSource::SOURCE_FLAG))
-     */
-    bitfield<CecRcProfileSource> profileSource;
-};
-
-/**
- * CEC Remote Control Profiles
- *
- * CEC 2.0 only use one byte to represent Remote Control Profile.
- */
-struct CecRcProfile {
-   CecRcProfile1 rcProfile1;
-};
-
-/**
- * CEC device power states as specified in CEC Table 11-10 of the CEC spec 2.0b
- */
-enum CecPowerState : int8_t {
-    ON = 0,
-    STANDBY = 1,
-    ON_TO_STANDBY = 2,
-    STANDBY_TO_ON = 4,
-    UNKNOWN = 0xFF, // some devices may not report power status
-};
-
-/** CEC physical address of device */
-typedef uint16_t CecPhysicalAddress;
-
-/**
- * CEC device information
- *
- * It is initially built during addressing specified in CEC section 11.3 of
- * the CEC spec 2.0b. It may be updated with cec devices's status changed.
- */
-struct CecDeviceInfo {
-    /** CEC version which device supports */
-    CecVersion version;
-
-    /** CEC device primary type */
-    CecDeviceType devceType;
-
-    /** CEC all device types */
-    CecAllDeviceTypes allDeviceTypes;
-
-    /** CEC device features */
-    CecDeviceFeatures deviceFeatures;
-
-    /** CEC Device Remote Control Profile */
-    CecRcProfile rcProfile;
-
-    /** CEC Device Vendor ID */
-    uint32_t vendorId;
-
-    /** logical address of device */
-    CecLogicalAddress logicalAddress;
-
-    /** physical of device */
-    CecPhysicalAddress physicalAddress;
-
-    /** power status of device */
-    CecPowerState powerState;
-};
-
-/**
- * Topology Event Type.
- */
-enum CecTopologyEventType : int32_t {
-    DEVICE_ADDED,
-    DEVICE_REMOVED,
-    DEVICE_UPDATED,
-};
-
-/**
- * Topology Event.
- */
-struct CecTopologyEvent {
-    CecTopologyEventType eventType;
-    CecLogicalAddress logicalAddress;
-    CecPhysicalAddress physicalAddress;
-
-    /** true if the event is about the device which the system run on */
-    bool isHostDevice;
-};
-
-
-/**
- * CEC UI Command Codes as specified in CEC Table 11-31 of the CEC spec 2.0b
- */
-enum CecUICommandCodes : int32_t {
-    SELECT_OK = 0x00,
-    UP = 0x01,
-    DOWN = 0x02,
-    LEFT = 0x03,
-    RIGHT = 0x04,
-    RIGHT_UP = 0x05,
-    RIGHT_DOWN = 0x06,
-    LEFT_UP = 0x07,
-    LEFT_DOWN = 0x08,
-    DEVICE_ROOT_MENU = 0x09,
-    DEVICE_SETUP_MENU = 0x0A,
-    CONTENTS_MENU = 0x0B,
-    FAVORITE_MENU = 0x0C,
-    BACK = 0x0D,
-    MEDIA_TOP_MENU = 0x10,
-    MEDIA_CONTEXT_SENSITIVE_MENU = 0x11,
-    NUMBER_ENTRY_MODE = 0x1D,
-    NUMBER_11 = 0x1E,
-    NUMBER_12 = 0x1F,
-    NUMBER_0 = 0x20, // or NUMBER 10
-    NUMBER_1 = 0x21,
-    NUMBER_2 = 0x22,
-    NUMBER_3 = 0x23,
-    NUMBER_4 = 0x24,
-    NUMBER_5 = 0x25,
-    NUMBER_6 = 0x26,
-    NUMBER_7 = 0x27,
-    NUMBER_8 = 0x28,
-    NUMBER_9 = 0x29,
-    DOT = 0x2A,
-    ENTER = 0x2B,
-    CLEAR = 0x2C,
-    NEXT_FAVORITE = 0x2F,
-    CHANNEL_UP = 0x30,
-    CHANNEL_DOWN = 0x31,
-    PREVIOUS_CHANNEL = 0x32,
-    SOUND_SELECT = 0x33,
-    INPUT_SELECT = 0x34,
-    DISPLAY_INFORMATION = 0x35,
-    HELP = 0x36,
-    PAGE_UP = 0x37,
-    PAGE_DOWN = 0x38,
-    POWER = 0x40,
-    VOLUME_UP = 0x41,
-    VOLUME_DOWN = 0x42,
-    MUTE = 0x43,
-    PLAY = 0x44,
-    STOP = 0x45,
-    PAUSE = 0x46,
-    RECORD = 0x47,
-    REWIND = 0x48,
-    FAST_FORWARD = 0x49,
-    EJECT = 0x4A,
-    SKIP_FORWARD = 0x4B,
-    SKIP_BACKWARD = 0x4C,
-    STOP_RECORD = 0x4D,
-    PAUSE_RECORD = 0x4E,
-    ANGLE = 0x50,
-    SUB_PICTURE = 0x51,
-    VIDEO_ON_DEMAND = 0x52,
-    ELECTRONIC_PROGRAM_GUIDE = 0x53,
-    TIMER_PROGRAMMING = 0x54,
-    INITIAL_CONFIGURATION = 0x55,
-    SELECT_BROADCAST_TYPE = 0x56,
-    SELECT_SOUND_PRESENTATION = 0x57,
-    AUDIO_DESCRIPTION = 0x58,
-    INTERNET = 0x59,
-    THREE_DIMENSIONAL_MODE = 0x5A,
-    PLAY_FUNCTION = 0x60,
-    PAUSE_PLAY_FUNCTION = 0x61,
-    RECORD_FUNCTION = 0x62,
-    PAUSE_RECORD_FUNCTION = 0x63,
-    STOP_FUNCTION = 0x64,
-    MUTE_FUNCTION = 0x65,
-    RESTORE_VOLUME_FUNCTION = 0x66,
-    TUNE_FUNCTION = 0x67,
-    SELECT_MEDIA_FUNCTION = 0x68,
-    SELECT_AV_INPUT_FUNCTION = 0x69,
-    SELECT_AUDIO_INPUT_FUNCTION = 0x6A,
-    POWER_TOGGLE_FUNCTION = 0x6B,
-    POWER_OFF_FUNCTION = 0x6C,
-    POWER_ON_FUNCTION = 0x6D,
-    F1 = 0x71, // BLUE
-    F2 = 0x72, // RED
-    F3 = 0x73, // GREEN
-    F4 = 0x74, // YELLOW
-    F5 = 0x75,
-    DATA = 0x76,
-};
-
-/**
- * HDMI port type.
- */
-enum HdmiPortType : int32_t {
-    INPUT = 0,
-    OUTPUT = 1,
-};
-
-/**
- * Options used for IHdmiCec.setOption()
- */
-enum OptionKey : int32_t {
-    /**
-     * When set to false, HAL does not wake up the system upon receiving <Image
-     * View On> or <Text View On>. Used when user changes the TV settings to
-     * disable the auto TV on functionality.
-     * Deprecated since <Image View On> and <Text View On> become mandatory
-     * featrues for CEC device. Use ENABLE_CEC OptionKey to disable CEC
-     * functionality instead.
-     * True by Default
-     */
-    WAKEUP = 1,
-
-    /**
-     * When set to false, all the CEC commands are discarded. if logical address
-     * is ever used, it shall be released. Used when user changes the TV
-     * settings to disable CEC functionality.
-     * True by default.
-     *
-     */
-    ENABLE_CEC = 2,
-
-    /**
-     * Setting this flag to false means Android system must stop handling CEC
-     * service and yield the control over to the microprocessor that is powered
-     * on through the standby mode.The microprocessor shall keep current logical
-     * and physical address. It shall response POLLING_MESSAGE, GIVE_FEATURES,
-     * GIVE_DEVICE_POWER_STATUS,GIVE_DEVICE_VENDOR_ID and GIVE_PHYSICAL_ADDRESS
-     * to allow other CEC devices to build CEC devices map specified in CEC
-     * section 11.3 of the CEC spec 2.0b.
-     * When set to true, the system must gain the control over, hence telling
-     * the microprocessor to start forwarding CEC messages to Android system.
-     * For example, this may be called when system goes in and out of
-     * standby mode to notify the microprocessor that it should start/stop
-     * handling CEC commands on behalf of the system.
-     * True by default.
-     */
-    SYSTEM_CEC_CONTROL = 3,
-
-    /* Option 4 not used */
-};
-
-/**
- * Hdmi port ID.
- *
- * It shall start from 1 which corresponds to HDMI "port 1".
- */
-typedef uint32_t HdmiPortId;
-
-/** Hdmi hotplug event */
-struct HotplugEvent {
-    bool connected;
-    HdmiPortId portId;
-};
-
-/**
- * HDMI port descriptor
- */
-struct HdmiPortInfo {
-    HdmiPortType type;
-    HdmiPortId portId;
-    bool cecSupported;
-    bool arcSupported;
-    CecPhysicalAddress physicalAddress;
-};
-
-enum Result : int32_t {
-    SUCCESS = 0,
-    FAILURE_UNKNOWN = 1,
-    FAILURE_INVALID_ARGS = 2,
-    FAILURE_INVALID_STATE = 3,
-    FAILURE_NOT_SUPPORTED = 4,
-    FAILURE_BUSY = 5,
-};
diff --git a/tv/input/1.0/Android.bp b/tv/input/1.0/Android.bp
index 1164430..292ec63 100644
--- a/tv/input/1.0/Android.bp
+++ b/tv/input/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tv.input@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ITvInput.hal",
diff --git a/tv/input/1.0/default/Android.bp b/tv/input/1.0/default/Android.bp
index 5f6b7e7..f572003 100644
--- a/tv/input/1.0/default/Android.bp
+++ b/tv/input/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.tv.input@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/tv/input/1.0/default/TvInput.cpp b/tv/input/1.0/default/TvInput.cpp
index 4ea1dec..7583a67 100644
--- a/tv/input/1.0/default/TvInput.cpp
+++ b/tv/input/1.0/default/TvInput.cpp
@@ -142,7 +142,7 @@
 
 // static
 void TvInput::notify(struct tv_input_device* __unused, tv_input_event_t* event,
-        void* __unused) {
+                     void* optionalStatus) {
     if (mCallback != nullptr && event != nullptr) {
         // Capturing is no longer supported.
         if (event->type >= TV_INPUT_EVENT_CAPTURE_SUCCEEDED) {
@@ -154,7 +154,17 @@
         tvInputEvent.deviceInfo.type = static_cast<TvInputType>(
                 event->device_info.type);
         tvInputEvent.deviceInfo.portId = event->device_info.hdmi.port_id;
-        tvInputEvent.deviceInfo.cableConnectionStatus = CableConnectionStatus::UNKNOWN;
+        CableConnectionStatus connectionStatus = CableConnectionStatus::UNKNOWN;
+        if (optionalStatus != nullptr &&
+            ((event->type == TV_INPUT_EVENT_STREAM_CONFIGURATIONS_CHANGED) ||
+             (event->type == TV_INPUT_EVENT_DEVICE_AVAILABLE))) {
+            int newStatus = *reinterpret_cast<int*>(optionalStatus);
+            if (newStatus <= static_cast<int>(CableConnectionStatus::DISCONNECTED) &&
+                newStatus >= static_cast<int>(CableConnectionStatus::UNKNOWN)) {
+                connectionStatus = static_cast<CableConnectionStatus>(newStatus);
+            }
+        }
+        tvInputEvent.deviceInfo.cableConnectionStatus = connectionStatus;
         // TODO: Ensure the legacy audio type code is the same once audio HAL default
         // implementation is ready.
         tvInputEvent.deviceInfo.audioType = static_cast<AudioDevice>(
diff --git a/tv/input/1.0/vts/functional/Android.bp b/tv/input/1.0/vts/functional/Android.bp
index 29d4e21..fad1c90 100644
--- a/tv/input/1.0/vts/functional/Android.bp
+++ b/tv/input/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalTvInputV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp b/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
index 59c70eb..15ea3e9 100644
--- a/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
+++ b/tv/input/1.0/vts/functional/VtsHalTvInputV1_0TargetTest.cpp
@@ -313,6 +313,9 @@
     tv_input_->openStream(device_id, stream_id,
                           [&result](Result res, const native_handle_t*) { result = res; });
     EXPECT_EQ(Result::INVALID_STATE, result);
+
+    // close stream as subsequent tests assume no open streams
+    EXPECT_EQ(Result::OK, tv_input_->closeStream(device_id, stream_id));
 }
 
 /*
@@ -337,3 +340,6 @@
         PerInstance, TvInputHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(ITvInput::descriptor)),
         android::hardware::PrintInstanceNameToString);
+
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TvInputHidlTest);
diff --git a/tv/tuner/1.0/Android.bp b/tv/tuner/1.0/Android.bp
index d78f3f2..237f495 100644
--- a/tv/tuner/1.0/Android.bp
+++ b/tv/tuner/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.tv.tuner@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IDemux.hal",
diff --git a/tv/tuner/1.0/default/Android.bp b/tv/tuner/1.0/default/Android.bp
index 5711889..ae15b6c 100644
--- a/tv/tuner/1.0/default/Android.bp
+++ b/tv/tuner/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_defaults {
     name: "tuner_service_defaults",
     defaults: ["hidl_defaults"],
@@ -24,7 +33,7 @@
         "libfmq",
         "libhidlbase",
         "libhidlmemory",
-        "libion",
+        "libdmabufheap",
         "liblog",
         "libstagefright_foundation",
         "libutils",
diff --git a/tv/tuner/1.0/default/Filter.cpp b/tv/tuner/1.0/default/Filter.cpp
index 30b19c0..a124a60 100644
--- a/tv/tuner/1.0/default/Filter.cpp
+++ b/tv/tuner/1.0/default/Filter.cpp
@@ -16,9 +16,11 @@
 
 #define LOG_TAG "android.hardware.tv.tuner@1.0-Filter"
 
-#include "Filter.h"
+#include <BufferAllocator/BufferAllocator.h>
 #include <utils/Log.h>
 
+#include "Filter.h"
+
 namespace android {
 namespace hardware {
 namespace tv {
@@ -603,15 +605,15 @@
 }
 
 int Filter::createAvIonFd(int size) {
-    // Create an ion fd and allocate an av fd mapped to a buffer to it.
-    int ion_fd = ion_open();
-    if (ion_fd == -1) {
-        ALOGE("[Filter] Failed to open ion fd %d", errno);
+    // Create an DMA-BUF fd and allocate an av fd mapped to a buffer to it.
+    auto buffer_allocator = std::make_unique<BufferAllocator>();
+    if (!buffer_allocator) {
+        ALOGE("[Filter] Unable to create BufferAllocator object");
         return -1;
     }
     int av_fd = -1;
-    ion_alloc_fd(dup(ion_fd), size, 0 /*align*/, ION_HEAP_SYSTEM_MASK, 0 /*flags*/, &av_fd);
-    if (av_fd == -1) {
+    av_fd = buffer_allocator->Alloc("system-uncached", size);
+    if (av_fd < 0) {
         ALOGE("[Filter] Failed to create av fd %d", errno);
         return -1;
     }
diff --git a/tv/tuner/1.0/default/Tuner.cpp b/tv/tuner/1.0/default/Tuner.cpp
index 48ce384..0430646 100644
--- a/tv/tuner/1.0/default/Tuner.cpp
+++ b/tv/tuner/1.0/default/Tuner.cpp
@@ -141,6 +141,8 @@
 
     // IP filter can be an MMTP filter's data source.
     caps.linkCaps = {0x00, 0x00, 0x02, 0x00, 0x00};
+    // Support time filter testing
+    caps.bTimeFilter = true;
     _hidl_cb(Result::SUCCESS, caps);
     return Void();
 }
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc
index ad72fae..ed62cee 100644
--- a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service-lazy.rc
@@ -6,4 +6,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+    task_profiles ProcessCapacityHigh
diff --git a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc
index 6d59ed7..5d5b943 100644
--- a/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc
+++ b/tv/tuner/1.0/default/android.hardware.tv.tuner@1.0-service.rc
@@ -3,4 +3,4 @@
     user media
     group mediadrm drmrpc
     ioprio rt 4
-    writepid /dev/cpuset/foreground/tasks
\ No newline at end of file
+    task_profiles ProcessCapacityHigh
diff --git a/tv/tuner/1.0/vts/functional/Android.bp b/tv/tuner/1.0/vts/functional/Android.bp
index 1765915..6187c73 100644
--- a/tv/tuner/1.0/vts/functional/Android.bp
+++ b/tv/tuner/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalTvTunerV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -26,6 +35,15 @@
         "DescramblerTests.cpp",
         "LnbTests.cpp",
     ],
+    generated_headers: [
+        "tuner_testing_dynamic_configuration_V1_0_enums",
+        "tuner_testing_dynamic_configuration_V1_0_parser",
+    ],
+    generated_sources: [
+        "tuner_testing_dynamic_configuration_V1_0_enums",
+        "tuner_testing_dynamic_configuration_V1_0_parser",
+    ],
+    header_libs: ["libxsdc-utils"],
     static_libs: [
         "android.hardware.cas@1.0",
         "android.hardware.cas@1.1",
@@ -40,6 +58,12 @@
     ],
     shared_libs: [
         "libbinder",
+        "libxml2",
+    ],
+    data: [
+        ":tuner_frontend_input_ts",
+        ":tuner_frontend_input_es",
+        ":tuner_testing_dynamic_configuration_V1_0",
     ],
     test_suites: [
         "general-tests",
diff --git a/tv/tuner/1.0/vts/functional/DescramblerTests.cpp b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
index 2e27475..67f6bae 100644
--- a/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DescramblerTests.cpp
@@ -53,12 +53,15 @@
         return failure();
     }
 
-    auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
-    if (status != android::hardware::cas::V1_0::Status::OK) {
-        ALOGW("[vts] Failed to set session private data");
-        mCas->closeSession(sessionId);
-        return failure();
+    if (hidlPvtData.size() > 0) {
+        auto status = mCas->setSessionPrivateData(sessionId, hidlPvtData);
+        if (status != android::hardware::cas::V1_0::Status::OK) {
+            ALOGW("[vts] Failed to set session private data");
+            mCas->closeSession(sessionId);
+            return failure();
+        }
     }
+
     return success();
 }
 
diff --git a/tv/tuner/1.0/vts/functional/DvrTests.cpp b/tv/tuner/1.0/vts/functional/DvrTests.cpp
index 0dfc032..ba21189 100644
--- a/tv/tuner/1.0/vts/functional/DvrTests.cpp
+++ b/tv/tuner/1.0/vts/functional/DvrTests.cpp
@@ -55,6 +55,7 @@
     uint8_t* buffer;
     ALOGW("[vts] playback thread loop start %s", mInputDataFile.c_str());
     if (fd < 0) {
+        EXPECT_TRUE(fd >= 0) << "Failed to open: " + mInputDataFile;
         mPlaybackThreadRunning = false;
         ALOGW("[vts] Error %s", strerror(errno));
     }
@@ -178,7 +179,7 @@
             // Our current implementation filter the data and write it into the filter FMQ
             // immediately after the DATA_READY from the VTS/framework
             if (!readRecordFMQ()) {
-                ALOGD("[vts] record data failed to be filtered. Ending thread");
+                ALOGW("[vts] record data failed to be filtered. Ending thread");
                 mRecordThreadRunning = false;
                 break;
             }
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.cpp b/tv/tuner/1.0/vts/functional/FilterTests.cpp
index 0ecdf73..a354c78 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FilterTests.cpp
@@ -70,6 +70,10 @@
 }
 
 bool FilterCallback::readFilterEventData() {
+    if (mFilterMQ == NULL) {
+        ALOGW("[vts] FMQ is not configured and does not need to be tested.");
+        return true;
+    }
     bool result = false;
     DemuxFilterEvent filterEvent = mFilterEvent;
     ALOGW("[vts] reading from filter FMQ or buffer %d", mFilterId);
@@ -218,7 +222,11 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult FilterTests::getFilterMQDescriptor(uint32_t filterId) {
+AssertionResult FilterTests::getFilterMQDescriptor(uint32_t filterId, bool getMqDesc) {
+    if (!getMqDesc) {
+        ALOGE("[vts] Filter does not need FMQ.");
+        return success();
+    }
     Result status;
     EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
     EXPECT_TRUE(mFilterCallbacks[filterId]) << "Test with getNewlyOpenedFilterId first.";
@@ -279,16 +287,14 @@
 AssertionResult FilterTests::closeFilter(uint32_t filterId) {
     EXPECT_TRUE(mFilters[filterId]) << "Test with getNewlyOpenedFilterId first.";
     Result status = mFilters[filterId]->close();
-    if (status == Result::SUCCESS) {
-        for (int i = 0; i < mUsedFilterIds.size(); i++) {
-            if (mUsedFilterIds[i] == filterId) {
-                mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
-                break;
-            }
+    for (int i = 0; i < mUsedFilterIds.size(); i++) {
+        if (mUsedFilterIds[i] == filterId) {
+            mUsedFilterIds.erase(mUsedFilterIds.begin() + i);
+            break;
         }
-        mFilterCallbacks.erase(filterId);
-        mFilters.erase(filterId);
     }
+    mFilterCallbacks.erase(filterId);
+    mFilters.erase(filterId);
     return AssertionResult(status == Result::SUCCESS);
 }
 
diff --git a/tv/tuner/1.0/vts/functional/FilterTests.h b/tv/tuner/1.0/vts/functional/FilterTests.h
index a76a6b9..75c59b3 100644
--- a/tv/tuner/1.0/vts/functional/FilterTests.h
+++ b/tv/tuner/1.0/vts/functional/FilterTests.h
@@ -157,7 +157,7 @@
     AssertionResult getTimeStamp();
     AssertionResult getNewlyOpenedFilterId(uint32_t& filterId);
     AssertionResult configFilter(DemuxFilterSettings setting, uint32_t filterId);
-    AssertionResult getFilterMQDescriptor(uint32_t filterId);
+    AssertionResult getFilterMQDescriptor(uint32_t filterId, bool getMqDesc);
     AssertionResult setFilterDataSource(uint32_t sourceFilterId, uint32_t sinkFilterId);
     AssertionResult setFilterDataSourceToDemux(uint32_t filterId);
     AssertionResult startFilter(uint32_t filterId);
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.cpp b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
index 45951d2..b35d112 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.cpp
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.cpp
@@ -370,13 +370,11 @@
     mIsSoftwareFe = config.isSoftwareFe;
     bool result = true;
     if (mIsSoftwareFe && testWithDemux) {
-        DvrConfig dvrConfig;
-        getSoftwareFrontendPlaybackConfig(dvrConfig);
-        result &= mDvrTests.openDvrInDemux(dvrConfig.type, dvrConfig.bufferSize) == success();
-        result &= mDvrTests.configDvrPlayback(dvrConfig.settings) == success();
+        result &= mDvrTests.openDvrInDemux(mDvrConfig.type, mDvrConfig.bufferSize) == success();
+        result &= mDvrTests.configDvrPlayback(mDvrConfig.settings) == success();
         result &= mDvrTests.getDvrPlaybackMQDescriptor() == success();
-        mDvrTests.startPlaybackInputThread(dvrConfig.playbackInputFile,
-                                           dvrConfig.settings.playback());
+        mDvrTests.startPlaybackInputThread(mDvrConfig.playbackInputFile,
+                                           mDvrConfig.settings.playback());
         if (!result) {
             ALOGW("[vts] Software frontend dvr configure failed.");
             return failure();
diff --git a/tv/tuner/1.0/vts/functional/FrontendTests.h b/tv/tuner/1.0/vts/functional/FrontendTests.h
index c536325..33ff603 100644
--- a/tv/tuner/1.0/vts/functional/FrontendTests.h
+++ b/tv/tuner/1.0/vts/functional/FrontendTests.h
@@ -104,6 +104,7 @@
     void setService(sp<ITuner> tuner) {
         mService = tuner;
         mDvrTests.setService(tuner);
+        getDefaultSoftwareFrontendPlaybackConfig(mDvrConfig);
     }
 
     AssertionResult getFrontendIds();
@@ -125,12 +126,14 @@
 
     void setDvrTests(DvrTests dvrTests) { mDvrTests = dvrTests; }
     void setDemux(sp<IDemux> demux) { mDvrTests.setDemux(demux); }
+    void setSoftwareFrontendDvrConfig(DvrConfig conf) { mDvrConfig = conf; }
 
   protected:
     static AssertionResult failure() { return ::testing::AssertionFailure(); }
     static AssertionResult success() { return ::testing::AssertionSuccess(); }
 
-    void getSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
+    // TODO: replace with customized dvr input
+    void getDefaultSoftwareFrontendPlaybackConfig(DvrConfig& dvrConfig) {
         PlaybackSettings playbackSettings{
                 .statusMask = 0xf,
                 .lowThreshold = 0x1000,
@@ -151,4 +154,5 @@
 
     DvrTests mDvrTests;
     bool mIsSoftwareFe = false;
+    DvrConfig mDvrConfig;
 };
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.cpp b/tv/tuner/1.0/vts/functional/LnbTests.cpp
index 9080f59..9338c73 100644
--- a/tv/tuner/1.0/vts/functional/LnbTests.cpp
+++ b/tv/tuner/1.0/vts/functional/LnbTests.cpp
@@ -48,10 +48,11 @@
     return AssertionResult(status == Result::SUCCESS);
 }
 
-AssertionResult LnbTests::openLnbByName(string lnbName) {
+AssertionResult LnbTests::openLnbByName(string lnbName, uint32_t& id) {
     Result status;
-    mService->openLnbByName(lnbName, [&](Result result, uint32_t /*lnbId*/, const sp<ILnb>& lnb) {
+    mService->openLnbByName(lnbName, [&](Result result, uint32_t lnbId, const sp<ILnb>& lnb) {
         mLnb = lnb;
+        id = lnbId;
         status = result;
     });
 
diff --git a/tv/tuner/1.0/vts/functional/LnbTests.h b/tv/tuner/1.0/vts/functional/LnbTests.h
index 2fdbe2c..62b42ff 100644
--- a/tv/tuner/1.0/vts/functional/LnbTests.h
+++ b/tv/tuner/1.0/vts/functional/LnbTests.h
@@ -64,7 +64,7 @@
 
     AssertionResult getLnbIds(vector<uint32_t>& ids);
     AssertionResult openLnbById(uint32_t lnbId);
-    AssertionResult openLnbByName(string lnbName);
+    AssertionResult openLnbByName(string lnbName, uint32_t& lnbId);
     AssertionResult setLnbCallback();
     AssertionResult setVoltage(LnbVoltage voltage);
     AssertionResult setTone(LnbTone tone);
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
index 2be68b8..b39abe3 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.cpp
@@ -18,16 +18,15 @@
 
 namespace {
 
-AssertionResult TunerBroadcastHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerBroadcastHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
-AssertionResult TunerPlaybackHidlTest::filterDataOutputTest(vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerPlaybackHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
-AssertionResult TunerDescramblerHidlTest::filterDataOutputTest(
-        vector<string> /*goldenOutputFiles*/) {
+AssertionResult TunerDescramblerHidlTest::filterDataOutputTest() {
     return filterDataOutputTestBase(mFilterTests);
 }
 
@@ -48,7 +47,7 @@
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -57,13 +56,13 @@
 }
 
 void TunerFilterHidlTest::testTimeFilter(TimeFilterConfig filterConf) {
-    if (!filterConf.supportTimeFilter) {
-        return;
-    }
     uint32_t demuxId;
     sp<IDemux> demux;
+    DemuxCapabilities caps;
 
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    ASSERT_TRUE(mDemuxTests.getDemuxCaps(caps));
+    ASSERT_TRUE(caps.bTimeFilter);
     mFilterTests.setDemux(demux);
     ASSERT_TRUE(mFilterTests.openTimeFilterInDemux());
     ASSERT_TRUE(mFilterTests.setTimeStamp(filterConf.timeStamp));
@@ -81,17 +80,14 @@
     uint32_t filterId;
 
     mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    if (feId == INVALID_ID) {
-        // TODO broadcast test on Cuttlefish needs licensed ts input,
-        // these tests are runnable on vendor device with real frontend module
-        // or with manual ts installing and use DVBT frontend.
-        return;
-    }
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
     if (mLnbId) {
         ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
     }
+    if (frontendConf.isSoftwareFe) {
+        mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[live.dvrSoftwareFeId]);
+    }
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFrontendTests.setDemux(demux);
@@ -99,11 +95,11 @@
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
     // tune test
     ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+    ASSERT_TRUE(filterDataOutputTest());
     ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
@@ -114,14 +110,16 @@
 void TunerBroadcastHidlTest::broadcastSingleFilterTestWithLnb(FilterConfig filterConf,
                                                               FrontendConfig frontendConf,
                                                               LnbConfig lnbConf) {
-    vector<uint32_t> ids;
-    ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbConf.usingLnb) {
-        return;
+    if (lnbConf.name.compare(emptyHardwareId) == 0) {
+        vector<uint32_t> ids;
+        ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+        ASSERT_TRUE(ids.size() > 0);
+        ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+        mLnbId = &ids[0];
+    } else {
+        mLnbId = (uint32_t*)malloc(sizeof(uint32_t));
+        ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
     }
-    ASSERT_TRUE(ids.size() > 0);
-    ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
-    *mLnbId = ids[0];
     ASSERT_TRUE(mLnbTests.setLnbCallback());
     ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
     ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
@@ -145,11 +143,11 @@
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     mDvrTests.startPlaybackInputThread(dvrConf.playbackInputFile, dvrConf.settings.playback());
     ASSERT_TRUE(mDvrTests.startDvrPlayback());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
+    ASSERT_TRUE(filterDataOutputTest());
     mDvrTests.stopPlaybackThread();
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mDvrTests.stopDvrPlayback());
@@ -160,65 +158,104 @@
 
 void TunerRecordHidlTest::recordSingleFilterTest(FilterConfig filterConf,
                                                  FrontendConfig frontendConf, DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mDvrTests.setDemux(demux);
+
+    DvrConfig dvrSourceConfig;
+    if (mLnbId || record.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        if (mLnbId) {
+            ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
+        }
+        if (frontendConf.isSoftwareFe) {
+            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[record.dvrSoftwareFeId]);
+        }
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFrontendTests.setDvrTests(mDvrTests);
+    } else {
+        dvrSourceConfig = dvrMap[record.dvrSourceId];
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     uint32_t filterId;
     sp<IFilter> filter;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    if (mLnbId) {
-        ASSERT_TRUE(mFrontendTests.setLnb(*mLnbId));
-    }
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mDvrTests.setDemux(demux);
-    mFrontendTests.setDvrTests(mDvrTests);
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
     ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
     ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     filter = mFilterTests.getFilterById(filterId);
     ASSERT_TRUE(filter != nullptr);
     mDvrTests.startRecordOutputThread(dvrConf.settings.record());
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
     ASSERT_TRUE(mDvrTests.startDvrRecord());
     ASSERT_TRUE(mFilterTests.startFilter(filterId));
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    } else {
+        // Start DVR Source
+        mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+                                           dvrSourceConfig.settings.playback());
+        ASSERT_TRUE(mDvrTests.startDvrPlayback());
+    }
+
     mDvrTests.testRecordOutput();
     mDvrTests.stopRecordThread();
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    } else {
+        mDvrTests.stopPlaybackThread();
+        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+    }
+
     ASSERT_TRUE(mFilterTests.stopFilter(filterId));
     ASSERT_TRUE(mDvrTests.stopDvrRecord());
     ASSERT_TRUE(mDvrTests.detachFilterToDvr(filter));
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
+
+    if (mLnbId || record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    } else {
+        mDvrTests.closeDvrPlayback();
+    }
+
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
 void TunerRecordHidlTest::recordSingleFilterTestWithLnb(FilterConfig filterConf,
                                                         FrontendConfig frontendConf,
                                                         DvrConfig dvrConf, LnbConfig lnbConf) {
-    vector<uint32_t> ids;
-    ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbConf.usingLnb) {
-        return;
+    if (lnbConf.name.compare(emptyHardwareId) == 0) {
+        vector<uint32_t> ids;
+        ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+        ASSERT_TRUE(ids.size() > 0);
+        ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+        mLnbId = &ids[0];
+    } else {
+        mLnbId = (uint32_t*)malloc(sizeof(uint32_t));
+        ASSERT_TRUE(mLnbTests.openLnbByName(lnbConf.name, *mLnbId));
     }
-    ASSERT_TRUE(ids.size() > 0);
-    ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
-    *mLnbId = ids[0];
     ASSERT_TRUE(mLnbTests.setLnbCallback());
     ASSERT_TRUE(mLnbTests.setVoltage(lnbConf.voltage));
     ASSERT_TRUE(mLnbTests.setTone(lnbConf.tone));
     ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbConf.position));
+    for (auto msgName : lnbRecord.diseqcMsgs) {
+        ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+    }
     recordSingleFilterTest(filterConf, frontendConf, dvrConf);
     ASSERT_TRUE(mLnbTests.closeLnb());
     mLnbId = nullptr;
@@ -227,27 +264,38 @@
 void TunerRecordHidlTest::attachSingleFilterToRecordDvrTest(FilterConfig filterConf,
                                                             FrontendConfig frontendConf,
                                                             DvrConfig dvrConf) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+    mDvrTests.setDemux(demux);
+
+    DvrConfig dvrSourceConfig;
+    if (record.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    } else {
+        dvrSourceConfig = dvrMap[record.dvrSourceId];
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     uint32_t filterId;
     sp<IFilter> filter;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mDvrTests.setDemux(demux);
+
     ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrConf.type, dvrConf.bufferSize));
     ASSERT_TRUE(mDvrTests.configDvrRecord(dvrConf.settings));
     ASSERT_TRUE(mDvrTests.getDvrRecordMQDescriptor());
+
     ASSERT_TRUE(mFilterTests.openFilterInDemux(filterConf.type, filterConf.bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
     ASSERT_TRUE(mFilterTests.configFilter(filterConf.settings, filterId));
-    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId));
+    ASSERT_TRUE(mFilterTests.getFilterMQDescriptor(filterId, filterConf.getMqDesc));
     filter = mFilterTests.getFilterById(filterId);
     ASSERT_TRUE(filter != nullptr);
     ASSERT_TRUE(mDvrTests.attachFilterToDvr(filter));
@@ -259,33 +307,43 @@
     ASSERT_TRUE(mFilterTests.closeFilter(filterId));
     mDvrTests.closeDvrRecord();
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+    if (record.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 void TunerDescramblerHidlTest::scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                                       FrontendConfig frontendConf,
                                                       DescramblerConfig descConfig) {
-    uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
+    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
+
+    DvrConfig dvrSourceConfig;
+    if (descrambling.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        if (frontendConf.isSoftwareFe) {
+            mFrontendTests.setSoftwareFrontendDvrConfig(dvrMap[descrambling.dvrSoftwareFeId]);
+        }
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+        mFrontendTests.setDemux(demux);
+    } else {
+        dvrSourceConfig = dvrMap[descrambling.dvrSourceId];
+        mDvrTests.setDemux(demux);
+        ASSERT_TRUE(mDvrTests.openDvrInDemux(dvrSourceConfig.type, dvrSourceConfig.bufferSize));
+        ASSERT_TRUE(mDvrTests.configDvrPlayback(dvrSourceConfig.settings));
+        ASSERT_TRUE(mDvrTests.getDvrPlaybackMQDescriptor());
+    }
+
     set<uint32_t> filterIds;
     uint32_t filterId;
     set<struct FilterConfig>::iterator config;
     set<uint32_t>::iterator id;
-
-    mFrontendTests.getFrontendIdByType(frontendConf.type, feId);
-    if (feId == INVALID_ID) {
-        // TODO broadcast test on Cuttlefish needs licensed ts input,
-        // these tests are runnable on vendor device with real frontend module
-        // or with manual ts installing and use DVBT frontend.
-        return;
-    }
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
-    ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    mFrontendTests.setDemux(demux);
     for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
         ASSERT_TRUE(mFilterTests.openFilterInDemux((*config).type, (*config).bufferSize));
         ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(filterId));
@@ -296,7 +354,7 @@
     TunerKeyToken token;
     ASSERT_TRUE(mDescramblerTests.getKeyToken(descConfig.casSystemId, descConfig.provisionStr,
                                               descConfig.hidlPvtData, token));
-    ASSERT_TRUE(mDescramblerTests.setKeyToken(token));
+    mDescramblerTests.setKeyToken(token);
     vector<DemuxPid> pids;
     DemuxPid pid;
     for (config = mediaFilterConfs.begin(); config != mediaFilterConfs.end(); config++) {
@@ -308,10 +366,26 @@
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.startFilter(*id));
     }
-    // tune test
-    ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
-    ASSERT_TRUE(filterDataOutputTest(goldenOutputFiles));
-    ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+
+    if (descrambling.hasFrontendConnection) {
+        // tune test
+        ASSERT_TRUE(mFrontendTests.tuneFrontend(frontendConf, true /*testWithDemux*/));
+    } else {
+        // Start DVR Source
+        mDvrTests.startPlaybackInputThread(dvrSourceConfig.playbackInputFile,
+                                           dvrSourceConfig.settings.playback());
+        ASSERT_TRUE(mDvrTests.startDvrPlayback());
+    }
+
+    ASSERT_TRUE(filterDataOutputTest());
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.stopTuneFrontend(true /*testWithDemux*/));
+    } else {
+        mDvrTests.stopPlaybackThread();
+        ASSERT_TRUE(mDvrTests.stopDvrPlayback());
+    }
+
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.stopFilter(*id));
     }
@@ -322,59 +396,73 @@
     for (id = filterIds.begin(); id != filterIds.end(); id++) {
         ASSERT_TRUE(mFilterTests.closeFilter(*id));
     }
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    } else {
+        mDvrTests.closeDvrPlayback();
+    }
+
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
 }
 
 TEST_P(TunerFrontendHidlTest, TuneFrontend) {
     description("Tune one Frontend with specific setting and check Lock event");
-    mFrontendTests.tuneTest(frontendArray[DVBT]);
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    mFrontendTests.tuneTest(frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFrontendHidlTest, AutoScanFrontend) {
     description("Run an auto frontend scan with specific setting and check lock scanMessage");
-    mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_AUTO);
+    if (!scan.hasFrontendConnection) {
+        return;
+    }
+    mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_AUTO);
 }
 
 TEST_P(TunerFrontendHidlTest, BlindScanFrontend) {
     description("Run an blind frontend scan with specific setting and check lock scanMessage");
-    mFrontendTests.scanTest(frontendScanArray[SCAN_DVBT], FrontendScanType::SCAN_BLIND);
-}
-
-TEST_P(TunerLnbHidlTest, OpenLnbByName) {
-    description("Open and configure an Lnb with name then send a diseqc msg to it.");
-    ASSERT_TRUE(mLnbTests.openLnbByName(lnbArray[LNB_EXTERNAL].name));
-    ASSERT_TRUE(mLnbTests.setLnbCallback());
-    ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB_EXTERNAL].voltage));
-    ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB_EXTERNAL].tone));
-    ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB_EXTERNAL].position));
-    ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
-    ASSERT_TRUE(mLnbTests.closeLnb());
+    if (!scan.hasFrontendConnection) {
+        return;
+    }
+    mFrontendTests.scanTest(frontendMap[scan.frontendId], FrontendScanType::SCAN_BLIND);
 }
 
 TEST_P(TunerLnbHidlTest, SendDiseqcMessageToLnb) {
     description("Open and configure an Lnb with specific settings then send a diseqc msg to it.");
-    vector<uint32_t> ids;
-    ASSERT_TRUE(mLnbTests.getLnbIds(ids));
-    if (!lnbArray[LNB0].usingLnb) {
+    if (!lnbLive.support) {
         return;
     }
-    ASSERT_TRUE(ids.size() > 0);
-    ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+    if (lnbMap[lnbLive.lnbId].name.compare(emptyHardwareId) == 0) {
+        vector<uint32_t> ids;
+        ASSERT_TRUE(mLnbTests.getLnbIds(ids));
+        ASSERT_TRUE(ids.size() > 0);
+        ASSERT_TRUE(mLnbTests.openLnbById(ids[0]));
+    } else {
+        uint32_t id;
+        ASSERT_TRUE(mLnbTests.openLnbByName(lnbMap[lnbLive.lnbId].name, id));
+    }
     ASSERT_TRUE(mLnbTests.setLnbCallback());
-    ASSERT_TRUE(mLnbTests.setVoltage(lnbArray[LNB0].voltage));
-    ASSERT_TRUE(mLnbTests.setTone(lnbArray[LNB0].tone));
-    ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbArray[LNB0].position));
-    ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgArray[DISEQC_POWER_ON]));
+    ASSERT_TRUE(mLnbTests.setVoltage(lnbMap[lnbLive.lnbId].voltage));
+    ASSERT_TRUE(mLnbTests.setTone(lnbMap[lnbLive.lnbId].tone));
+    ASSERT_TRUE(mLnbTests.setSatellitePosition(lnbMap[lnbLive.lnbId].position));
+    for (auto msgName : lnbLive.diseqcMsgs) {
+        ASSERT_TRUE(mLnbTests.sendDiseqcMessage(diseqcMsgMap[msgName]));
+    }
     ASSERT_TRUE(mLnbTests.closeLnb());
 }
 
 TEST_P(TunerDemuxHidlTest, openDemux) {
     description("Open and close a Demux.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
@@ -386,6 +474,12 @@
 
 TEST_P(TunerDemuxHidlTest, getAvSyncTime) {
     description("Get the A/V sync time from a PCR filter.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    if (live.pcrFilterId.compare(emptyHardwareId) == 0) {
+        return;
+    }
     uint32_t feId;
     uint32_t demuxId;
     sp<IDemux> demux;
@@ -394,22 +488,22 @@
     uint32_t avSyncHwId;
     sp<IFilter> mediaFilter;
 
-    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
+    mFrontendTests.getFrontendIdByType(frontendMap[live.frontendId].type, feId);
     ASSERT_TRUE(feId != INVALID_ID);
     ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
     ASSERT_TRUE(mFrontendTests.setFrontendCallback());
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
     ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
     mFilterTests.setDemux(demux);
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_VIDEO1].type,
-                                               filterArray[TS_VIDEO1].bufferSize));
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.videoFilterId].type,
+                                               filterMap[live.videoFilterId].bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(mediaFilterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_VIDEO1].settings, mediaFilterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.videoFilterId].settings, mediaFilterId));
     mediaFilter = mFilterTests.getFilterById(mediaFilterId);
-    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterArray[TS_PCR0].type,
-                                               filterArray[TS_PCR0].bufferSize));
+    ASSERT_TRUE(mFilterTests.openFilterInDemux(filterMap[live.pcrFilterId].type,
+                                               filterMap[live.pcrFilterId].bufferSize));
     ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(pcrFilterId));
-    ASSERT_TRUE(mFilterTests.configFilter(filterArray[TS_PCR0].settings, pcrFilterId));
+    ASSERT_TRUE(mFilterTests.configFilter(filterMap[live.pcrFilterId].settings, pcrFilterId));
     ASSERT_TRUE(mDemuxTests.getAvSyncId(mediaFilter, avSyncHwId));
     ASSERT_TRUE(pcrFilterId == avSyncHwId);
     ASSERT_TRUE(mDemuxTests.getAvSyncTime(pcrFilterId));
@@ -421,8 +515,11 @@
 
 TEST_P(TunerFilterHidlTest, StartFilterInDemux) {
     description("Open and start a filter in Demux.");
+    if (!live.hasFrontendConnection) {
+        return;
+    }
     // TODO use paramterized tests
-    configSingleFilterInDemuxTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+    configSingleFilterInDemuxTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerFilterHidlTest, SetFilterLinkage) {
@@ -439,11 +536,9 @@
             if (caps.linkCaps[i] & (bitMask << j)) {
                 uint32_t sourceFilterId;
                 uint32_t sinkFilterId;
-                ASSERT_TRUE(mFilterTests.openFilterInDemux(filterLinkageTypes[SOURCE][i],
-                                                           FMQ_SIZE_16M));
+                ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(i), FMQ_SIZE_16M));
                 ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sourceFilterId));
-                ASSERT_TRUE(
-                        mFilterTests.openFilterInDemux(filterLinkageTypes[SINK][j], FMQ_SIZE_16M));
+                ASSERT_TRUE(mFilterTests.openFilterInDemux(getLinkageFilterType(j), FMQ_SIZE_16M));
                 ASSERT_TRUE(mFilterTests.getNewlyOpenedFilterId(sinkFilterId));
                 ASSERT_TRUE(mFilterTests.setFilterDataSource(sourceFilterId, sinkFilterId));
                 ASSERT_TRUE(mFilterTests.setFilterDataSourceToDemux(sinkFilterId));
@@ -457,80 +552,147 @@
 
 TEST_P(TunerFilterHidlTest, testTimeFilter) {
     description("Open a timer filter in Demux and set time stamp.");
+    if (!timeFilter.support) {
+        return;
+    }
     // TODO use paramterized tests
-    testTimeFilter(timeFilterArray[TIMER0]);
+    testTimeFilter(timeFilterMap[timeFilter.timeFilterId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO1], frontendArray[DVBT]);
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowAudioFilterTest) {
     description("Test Audio Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_AUDIO0], frontendArray[DVBT]);
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    broadcastSingleFilterTest(filterMap[live.audioFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, BroadcastDataFlowSectionFilterTest) {
     description("Test Section Filter functionality in Broadcast use case.");
-    broadcastSingleFilterTest(filterArray[TS_SECTION0], frontendArray[DVBT]);
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    if (live.sectionFilterId.compare(emptyHardwareId) == 0) {
+        return;
+    }
+    broadcastSingleFilterTest(filterMap[live.sectionFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, IonBufferTest) {
     description("Test the av filter data bufferring.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBT]);
+    if (!live.hasFrontendConnection) {
+        return;
+    }
+    broadcastSingleFilterTest(filterMap[live.videoFilterId], frontendMap[live.frontendId]);
 }
 
 TEST_P(TunerBroadcastHidlTest, LnbBroadcastDataFlowVideoFilterTest) {
     description("Test Video Filter functionality in Broadcast with Lnb use case.");
-    broadcastSingleFilterTest(filterArray[TS_VIDEO0], frontendArray[DVBS]);
+    if (!lnbLive.support) {
+        return;
+    }
+    broadcastSingleFilterTestWithLnb(filterMap[lnbLive.videoFilterId],
+                                     frontendMap[lnbLive.frontendId], lnbMap[lnbLive.lnbId]);
 }
 
 TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsSectionFilterTest) {
     description("Feed ts data from playback and configure Ts section filter to get output");
-    playbackSingleFilterTest(filterArray[TS_SECTION0], dvrArray[DVR_PLAYBACK0]);
+    if (!playback.support || playback.sectionFilterId.compare(emptyHardwareId) == 0) {
+        return;
+    }
+    playbackSingleFilterTest(filterMap[playback.sectionFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsAudioFilterTest) {
+    description("Feed ts data from playback and configure Ts audio filter to get output");
+    if (!playback.support) {
+        return;
+    }
+    playbackSingleFilterTest(filterMap[playback.audioFilterId], dvrMap[playback.dvrId]);
+}
+
+TEST_P(TunerPlaybackHidlTest, PlaybackDataFlowWithTsVideoFilterTest) {
+    description("Feed ts data from playback and configure Ts video filter to get output");
+    if (!playback.support) {
+        return;
+    }
+    playbackSingleFilterTest(filterMap[playback.videoFilterId], dvrMap[playback.dvrId]);
 }
 
 TEST_P(TunerRecordHidlTest, AttachFiltersToRecordTest) {
     description("Attach a single filter to the record dvr test.");
     // TODO use paramterized tests
-    attachSingleFilterToRecordDvrTest(filterArray[TS_RECORD0], frontendArray[DVBT],
-                                      dvrArray[DVR_RECORD0]);
+    if (!record.support) {
+        return;
+    }
+    attachSingleFilterToRecordDvrTest(filterMap[record.recordFilterId],
+                                      frontendMap[record.frontendId], dvrMap[record.dvrRecordId]);
 }
 
 TEST_P(TunerRecordHidlTest, RecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from frontend to recording and test with ts record filter");
-    recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBT], dvrArray[DVR_RECORD0]);
+    if (!record.support) {
+        return;
+    }
+    recordSingleFilterTest(filterMap[record.recordFilterId], frontendMap[record.frontendId],
+                           dvrMap[record.dvrRecordId]);
 }
 
 TEST_P(TunerRecordHidlTest, LnbRecordDataFlowWithTsRecordFilterTest) {
     description("Feed ts data from Fe with Lnb to recording and test with ts record filter");
-    recordSingleFilterTest(filterArray[TS_RECORD0], frontendArray[DVBS], dvrArray[DVR_RECORD0]);
+    if (!lnbRecord.support) {
+        return;
+    }
+    recordSingleFilterTestWithLnb(filterMap[lnbRecord.recordFilterId],
+                                  frontendMap[lnbRecord.frontendId], dvrMap[lnbRecord.dvrRecordId],
+                                  lnbMap[lnbRecord.lnbId]);
 }
 
 TEST_P(TunerDescramblerHidlTest, CreateDescrambler) {
     description("Create Descrambler");
-    uint32_t feId;
+    if (!descrambling.support) {
+        return;
+    }
     uint32_t demuxId;
     sp<IDemux> demux;
-    mFrontendTests.getFrontendIdByType(frontendArray[DVBT].type, feId);
-    ASSERT_TRUE(feId != INVALID_ID);
-    ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
-    ASSERT_TRUE(mFrontendTests.setFrontendCallback());
     ASSERT_TRUE(mDemuxTests.openDemux(demux, demuxId));
-    ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+
+    if (descrambling.hasFrontendConnection) {
+        uint32_t feId;
+        mFrontendTests.getFrontendIdByType(frontendMap[descrambling.frontendId].type, feId);
+        ASSERT_TRUE(feId != INVALID_ID);
+        ASSERT_TRUE(mFrontendTests.openFrontendById(feId));
+        ASSERT_TRUE(mFrontendTests.setFrontendCallback());
+        ASSERT_TRUE(mDemuxTests.setDemuxFrontendDataSource(feId));
+    }
+
     ASSERT_TRUE(mDescramblerTests.openDescrambler(demuxId));
     ASSERT_TRUE(mDescramblerTests.closeDescrambler());
     ASSERT_TRUE(mDemuxTests.closeDemux());
-    ASSERT_TRUE(mFrontendTests.closeFrontend());
+
+    if (descrambling.hasFrontendConnection) {
+        ASSERT_TRUE(mFrontendTests.closeFrontend());
+    }
 }
 
 TEST_P(TunerDescramblerHidlTest, ScrambledBroadcastDataFlowMediaFiltersTest) {
     description("Test ts audio filter in scrambled broadcast use case");
+    if (!descrambling.support) {
+        return;
+    }
     set<FilterConfig> filterConfs;
-    filterConfs.insert(filterArray[TS_AUDIO0]);
-    filterConfs.insert(filterArray[TS_VIDEO1]);
-    scrambledBroadcastTest(filterConfs, frontendArray[DVBT], descramblerArray[DESC_0]);
+    filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.audioFilterId]));
+    filterConfs.insert(static_cast<FilterConfig>(filterMap[descrambling.videoFilterId]));
+    scrambledBroadcastTest(filterConfs, frontendMap[descrambling.frontendId],
+                           descramblerMap[descrambling.descramblerId]);
 }
 
 INSTANTIATE_TEST_SUITE_P(
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
index 6804f3c..e240604 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TargetTest.h
@@ -20,6 +20,9 @@
 #include "LnbTests.h"
 
 using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
 using android::hardware::tv::tuner::V1_0::IDescrambler;
 
 static AssertionResult success() {
@@ -28,14 +31,22 @@
 
 namespace {
 
-void initConfiguration() {
+bool initConfiguration() {
+    if (!TunerTestingConfigReader::checkConfigFileExists()) {
+        return false;
+    }
     initFrontendConfig();
-    initFrontendScanConfig();
-    initLnbConfig();
     initFilterConfig();
-    initTimeFilterConfig();
     initDvrConfig();
+    initLnbConfig();
+    initTimeFilterConfig();
     initDescramblerConfig();
+    connectHardwaresToTestCases();
+    if (!validateConnections()) {
+        ALOGW("[vts] failed to validate connections.");
+        return false;
+    }
+    return true;
 }
 
 AssertionResult filterDataOutputTestBase(FilterTests tests) {
@@ -53,7 +64,7 @@
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
     }
@@ -67,12 +78,15 @@
     FrontendTests mFrontendTests;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFrontendHidlTest);
+
 class TunerLnbHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mLnbTests.setService(mService);
     }
@@ -86,12 +100,15 @@
     LnbTests mLnbTests;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerLnbHidlTest);
+
 class TunerDemuxHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -109,12 +126,15 @@
     FilterTests mFilterTests;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDemuxHidlTest);
+
 class TunerFilterHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -129,18 +149,44 @@
     void configSingleFilterInDemuxTest(FilterConfig filterConf, FrontendConfig frontendConf);
     void testTimeFilter(TimeFilterConfig filterConf);
 
+    DemuxFilterType getLinkageFilterType(int bit) {
+        DemuxFilterType type;
+        type.mainType = static_cast<DemuxFilterMainType>(1 << bit);
+        switch (type.mainType) {
+            case DemuxFilterMainType::TS:
+                type.subType.tsFilterType(DemuxTsFilterType::UNDEFINED);
+                break;
+            case DemuxFilterMainType::MMTP:
+                type.subType.mmtpFilterType(DemuxMmtpFilterType::UNDEFINED);
+                break;
+            case DemuxFilterMainType::IP:
+                type.subType.ipFilterType(DemuxIpFilterType::UNDEFINED);
+                break;
+            case DemuxFilterMainType::TLV:
+                type.subType.tlvFilterType(DemuxTlvFilterType::UNDEFINED);
+                break;
+            case DemuxFilterMainType::ALP:
+                type.subType.alpFilterType(DemuxAlpFilterType::UNDEFINED);
+                break;
+        }
+        return type;
+    }
+
     sp<ITuner> mService;
     FrontendTests mFrontendTests;
     DemuxTests mDemuxTests;
     FilterTests mFilterTests;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerFilterHidlTest);
+
 class TunerBroadcastHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -161,7 +207,7 @@
     LnbTests mLnbTests;
     DvrTests mDvrTests;
 
-    AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+    AssertionResult filterDataOutputTest();
 
     void broadcastSingleFilterTest(FilterConfig filterConf, FrontendConfig frontendConf);
     void broadcastSingleFilterTestWithLnb(FilterConfig filterConf, FrontendConfig frontendConf,
@@ -171,12 +217,15 @@
     uint32_t* mLnbId = nullptr;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerBroadcastHidlTest);
+
 class TunerPlaybackHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -195,17 +244,20 @@
     FilterTests mFilterTests;
     DvrTests mDvrTests;
 
-    AssertionResult filterDataOutputTest(vector<string> goldenOutputFiles);
+    AssertionResult filterDataOutputTest();
 
     void playbackSingleFilterTest(FilterConfig filterConf, DvrConfig dvrConf);
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerPlaybackHidlTest);
+
 class TunerRecordHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
         mService = ITuner::getService(GetParam());
         ASSERT_NE(mService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -237,6 +289,9 @@
     uint32_t* mLnbId = nullptr;
 };
 
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerRecordHidlTest);
+
 class TunerDescramblerHidlTest : public testing::TestWithParam<std::string> {
   public:
     virtual void SetUp() override {
@@ -244,7 +299,7 @@
         mCasService = IMediaCasService::getService();
         ASSERT_NE(mService, nullptr);
         ASSERT_NE(mCasService, nullptr);
-        initConfiguration();
+        ASSERT_TRUE(initConfiguration());
 
         mFrontendTests.setService(mService);
         mDemuxTests.setService(mService);
@@ -260,7 +315,7 @@
 
     void scrambledBroadcastTest(set<struct FilterConfig> mediaFilterConfs,
                                 FrontendConfig frontendConf, DescramblerConfig descConfig);
-    AssertionResult filterDataOutputTest(vector<string> /*goldenOutputFiles*/);
+    AssertionResult filterDataOutputTest();
 
     sp<ITuner> mService;
     sp<IMediaCasService> mCasService;
@@ -270,4 +325,7 @@
     DescramblerTests mDescramblerTests;
     DvrTests mDvrTests;
 };
+
+// TODO remove from the allow list once the cf tv target is enabled for testing
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TunerDescramblerHidlTest);
 }  // namespace
diff --git a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
index 6c68e35..8328e0c 100644
--- a/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
+++ b/tv/tuner/1.0/vts/functional/VtsHalTvTunerV1_0TestConfigurations.h
@@ -21,363 +21,258 @@
 #include <hidl/Status.h>
 #include <hidlmemory/FrameworkUtils.h>
 
-using android::hardware::tv::tuner::V1_0::DataFormat;
-using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+#include "../../../config/TunerTestingConfigReader.h"
+
 using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
-using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
-using android::hardware::tv::tuner::V1_0::DemuxFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
-using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
-using android::hardware::tv::tuner::V1_0::DemuxTpid;
 using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
-using android::hardware::tv::tuner::V1_0::DvrSettings;
-using android::hardware::tv::tuner::V1_0::DvrType;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
-using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
 using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
 using android::hardware::tv::tuner::V1_0::FrontendSettings;
 using android::hardware::tv::tuner::V1_0::FrontendStatus;
 using android::hardware::tv::tuner::V1_0::FrontendStatusType;
 using android::hardware::tv::tuner::V1_0::FrontendType;
-using android::hardware::tv::tuner::V1_0::LnbPosition;
-using android::hardware::tv::tuner::V1_0::LnbTone;
-using android::hardware::tv::tuner::V1_0::LnbVoltage;
-using android::hardware::tv::tuner::V1_0::PlaybackSettings;
-using android::hardware::tv::tuner::V1_0::RecordSettings;
 
 using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
 
-const uint32_t FMQ_SIZE_1M = 0x100000;
 const uint32_t FMQ_SIZE_4M = 0x400000;
 const uint32_t FMQ_SIZE_16M = 0x1000000;
 
-#define CLEAR_KEY_SYSTEM_ID 0xF6D8
-#define FILTER_MAIN_TYPE_BIT_COUNT 32
-#define PROVISION_STR                                      \
-    "{                                                   " \
-    "  \"id\": 21140844,                                 " \
-    "  \"name\": \"Test Title\",                         " \
-    "  \"lowercase_organization_name\": \"Android\",     " \
-    "  \"asset_key\": {                                  " \
-    "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " \
-    "  },                                                " \
-    "  \"cas_type\": 1,                                  " \
-    "  \"track_types\": [ ]                              " \
-    "}                                                   "
+#define FILTER_MAIN_TYPE_BIT_COUNT 5
 
-typedef enum {
-    TS_VIDEO0,
-    TS_VIDEO1,
-    TS_AUDIO0,
-    TS_PES0,
-    TS_PCR0,
-    TS_SECTION0,
-    TS_TS0,
-    TS_RECORD0,
-    FILTER_MAX,
-} Filter;
+// Hardware configs
+static map<string, FrontendConfig> frontendMap;
+static map<string, FilterConfig> filterMap;
+static map<string, DvrConfig> dvrMap;
+static map<string, LnbConfig> lnbMap;
+static map<string, TimeFilterConfig> timeFilterMap;
+static map<string, vector<uint8_t>> diseqcMsgMap;
+static map<string, DescramblerConfig> descramblerMap;
 
-typedef enum {
-    TIMER0,
-    TIMER_MAX,
-} TimeFilter;
+// Hardware and test cases connections
+static LiveBroadcastHardwareConnections live;
+static ScanHardwareConnections scan;
+static DvrPlaybackHardwareConnections playback;
+static DvrRecordHardwareConnections record;
+static DescramblingHardwareConnections descrambling;
+static LnbLiveHardwareConnections lnbLive;
+static LnbRecordHardwareConnections lnbRecord;
+static TimeFilterHardwareConnections timeFilter;
 
-typedef enum {
-    SOURCE,
-    SINK,
-    LINKAGE_DIR,
-} Linkage;
-
-typedef enum {
-    DVBT,
-    DVBS,
-    FRONTEND_MAX,
-} Frontend;
-
-typedef enum {
-    LNB0,
-    LNB_EXTERNAL,
-    LNB_MAX,
-} Lnb;
-
-typedef enum {
-    DISEQC_POWER_ON,
-    DISEQC_MAX,
-} Diseqc;
-
-typedef enum {
-    SCAN_DVBT,
-    SCAN_MAX,
-} FrontendScan;
-
-typedef enum {
-    DVR_RECORD0,
-    DVR_PLAYBACK0,
-    DVR_SOFTWARE_FE,
-    DVR_MAX,
-} Dvr;
-
-typedef enum {
-    DESC_0,
-    DESC_MAX,
-} Descrambler;
-
-struct FilterConfig {
-    uint32_t bufferSize;
-    DemuxFilterType type;
-    DemuxFilterSettings settings;
-
-    bool operator<(const FilterConfig& /*c*/) const { return false; }
-};
-
-struct TimeFilterConfig {
-    bool supportTimeFilter;
-    uint64_t timeStamp;
-};
-
-struct FrontendConfig {
-    bool isSoftwareFe;
-    FrontendType type;
-    FrontendSettings settings;
-    vector<FrontendStatusType> tuneStatusTypes;
-    vector<FrontendStatus> expectTuneStatuses;
-};
-
-struct LnbConfig {
-    bool usingLnb;
-    string name;
-    LnbVoltage voltage;
-    LnbTone tone;
-    LnbPosition position;
-};
-
-struct ChannelConfig {
-    int32_t frontendId;
-    int32_t channelId;
-    std::string channelName;
-    DemuxTpid videoPid;
-    DemuxTpid audioPid;
-};
-
-struct DvrConfig {
-    DvrType type;
-    uint32_t bufferSize;
-    DvrSettings settings;
-    string playbackInputFile;
-};
-
-struct DescramblerConfig {
-    uint32_t casSystemId;
-    string provisionStr;
-    vector<uint8_t> hidlPvtData;
-};
-
-static FrontendConfig frontendArray[FILTER_MAX];
-static FrontendConfig frontendScanArray[SCAN_MAX];
-static LnbConfig lnbArray[LNB_MAX];
-static vector<uint8_t> diseqcMsgArray[DISEQC_MAX];
-static ChannelConfig channelArray[FRONTEND_MAX];
-static FilterConfig filterArray[FILTER_MAX];
-static TimeFilterConfig timeFilterArray[TIMER_MAX];
-static DemuxFilterType filterLinkageTypes[LINKAGE_DIR][FILTER_MAIN_TYPE_BIT_COUNT];
-static DvrConfig dvrArray[DVR_MAX];
-static DescramblerConfig descramblerArray[DESC_MAX];
-static vector<string> goldenOutputFiles;
-
-/** Configuration array for the frontend tune test */
+/** Config all the frontends that would be used in the tests */
 inline void initFrontendConfig() {
+    // The test will use the internal default fe when default fe is connected to any data flow
+    // without overriding in the xml config.
+    string defaultFeId = "FE_DEFAULT";
     FrontendDvbtSettings dvbtSettings{
             .frequency = 578000,
             .transmissionMode = FrontendDvbtTransmissionMode::AUTO,
             .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
-            .constellation = FrontendDvbtConstellation::AUTO,
-            .hierarchy = FrontendDvbtHierarchy::AUTO,
-            .hpCoderate = FrontendDvbtCoderate::AUTO,
-            .lpCoderate = FrontendDvbtCoderate::AUTO,
-            .guardInterval = FrontendDvbtGuardInterval::AUTO,
             .isHighPriority = true,
-            .standard = FrontendDvbtStandard::T,
     };
-    frontendArray[DVBT].type = FrontendType::DVBT, frontendArray[DVBT].settings.dvbt(dvbtSettings);
+    frontendMap[defaultFeId].type = FrontendType::DVBT;
+    frontendMap[defaultFeId].settings.dvbt(dvbtSettings);
+
     vector<FrontendStatusType> types;
     types.push_back(FrontendStatusType::DEMOD_LOCK);
     FrontendStatus status;
     status.isDemodLocked(true);
     vector<FrontendStatus> statuses;
     statuses.push_back(status);
-    frontendArray[DVBT].tuneStatusTypes = types;
-    frontendArray[DVBT].expectTuneStatuses = statuses;
-    frontendArray[DVBT].isSoftwareFe = true;
-    frontendArray[DVBS].type = FrontendType::DVBS;
-    frontendArray[DVBS].isSoftwareFe = true;
+    frontendMap[defaultFeId].tuneStatusTypes = types;
+    frontendMap[defaultFeId].expectTuneStatuses = statuses;
+    frontendMap[defaultFeId].isSoftwareFe = true;
+
+    // Read customized config
+    TunerTestingConfigReader::readFrontendConfig1_0(frontendMap);
 };
 
-/** Configuration array for the frontend scan test */
-inline void initFrontendScanConfig() {
-    frontendScanArray[SCAN_DVBT].type = FrontendType::DVBT;
-    frontendScanArray[SCAN_DVBT].settings.dvbt({
-            .frequency = 578000,
-            .transmissionMode = FrontendDvbtTransmissionMode::MODE_8K,
-            .bandwidth = FrontendDvbtBandwidth::BANDWIDTH_8MHZ,
-            .constellation = FrontendDvbtConstellation::AUTO,
-            .hierarchy = FrontendDvbtHierarchy::AUTO,
-            .hpCoderate = FrontendDvbtCoderate::AUTO,
-            .lpCoderate = FrontendDvbtCoderate::AUTO,
-            .guardInterval = FrontendDvbtGuardInterval::AUTO,
-            .isHighPriority = true,
-            .standard = FrontendDvbtStandard::T,
-    });
-};
-
-/** Configuration array for the Lnb test */
-inline void initLnbConfig() {
-    lnbArray[LNB0].usingLnb = true;
-    lnbArray[LNB0].voltage = LnbVoltage::VOLTAGE_12V;
-    lnbArray[LNB0].tone = LnbTone::NONE;
-    lnbArray[LNB0].position = LnbPosition::UNDEFINED;
-    lnbArray[LNB_EXTERNAL].usingLnb = true;
-    lnbArray[LNB_EXTERNAL].name = "default_lnb_external";
-    lnbArray[LNB_EXTERNAL].voltage = LnbVoltage::VOLTAGE_5V;
-    lnbArray[LNB_EXTERNAL].tone = LnbTone::NONE;
-    lnbArray[LNB_EXTERNAL].position = LnbPosition::UNDEFINED;
-};
-
-/** Diseqc messages array for the Lnb test */
-inline void initDiseqcMsg() {
-    diseqcMsgArray[DISEQC_POWER_ON] = {0xE, 0x0, 0x0, 0x0, 0x0, 0x3};
-};
-
-/** Configuration array for the filter test */
 inline void initFilterConfig() {
-    // TS VIDEO filter setting for default implementation testing
-    filterArray[TS_VIDEO0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_VIDEO0].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_VIDEO0].settings.ts().tpid = 256;
-    filterArray[TS_VIDEO0].settings.ts().filterSettings.av({.isPassthrough = false});
-    filterArray[TS_VIDEO1].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_VIDEO1].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
-    filterArray[TS_VIDEO1].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_VIDEO1].settings.ts().tpid = 256;
-    filterArray[TS_VIDEO1].settings.ts().filterSettings.av({.isPassthrough = false});
-    // TS AUDIO filter setting
-    filterArray[TS_AUDIO0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_AUDIO0].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
-    filterArray[TS_AUDIO0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_AUDIO0].settings.ts().tpid = 256;
-    filterArray[TS_AUDIO0].settings.ts().filterSettings.av({.isPassthrough = false});
-    // TS PES filter setting
-    filterArray[TS_PES0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_PES0].type.subType.tsFilterType(DemuxTsFilterType::PES);
-    filterArray[TS_PES0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_PES0].settings.ts().tpid = 256;
-    filterArray[TS_PES0].settings.ts().filterSettings.pesData({
-            .isRaw = false,
-            .streamId = 0xbd,
-    });
-    // TS PCR filter setting
-    filterArray[TS_PCR0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_PCR0].type.subType.tsFilterType(DemuxTsFilterType::PCR);
-    filterArray[TS_PCR0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_PCR0].settings.ts().tpid = 256;
-    filterArray[TS_PCR0].settings.ts().filterSettings.noinit();
-    // TS filter setting
-    filterArray[TS_TS0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_TS0].type.subType.tsFilterType(DemuxTsFilterType::TS);
-    filterArray[TS_TS0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_TS0].settings.ts().tpid = 256;
-    filterArray[TS_TS0].settings.ts().filterSettings.noinit();
-    // TS SECTION filter setting
-    filterArray[TS_SECTION0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_SECTION0].type.subType.tsFilterType(DemuxTsFilterType::SECTION);
-    filterArray[TS_SECTION0].bufferSize = FMQ_SIZE_16M;
-    filterArray[TS_SECTION0].settings.ts().tpid = 256;
-    filterArray[TS_SECTION0].settings.ts().filterSettings.section({
-            .isRaw = false,
-    });
-    // TS RECORD filter setting
-    filterArray[TS_RECORD0].type.mainType = DemuxFilterMainType::TS;
-    filterArray[TS_RECORD0].type.subType.tsFilterType(DemuxTsFilterType::RECORD);
-    filterArray[TS_RECORD0].settings.ts().tpid = 81;
-    filterArray[TS_RECORD0].settings.ts().filterSettings.record({
-            .scIndexType = DemuxRecordScIndexType::NONE,
-    });
+    // The test will use the internal default filter when default filter is connected to any
+    // data flow without overriding in the xml config.
+    string defaultAudioFilterId = "FILTER_AUDIO_DEFAULT";
+    string defaultVideoFilterId = "FILTER_VIDEO_DEFAULT";
 
-    // TS Linkage filter setting
-    filterLinkageTypes[SOURCE][0].mainType = DemuxFilterMainType::TS;
-    filterLinkageTypes[SOURCE][0].subType.tsFilterType(DemuxTsFilterType::TS);
-    filterLinkageTypes[SINK][0] = filterLinkageTypes[SOURCE][0];
-    // MMTP Linkage filter setting
-    filterLinkageTypes[SOURCE][1].mainType = DemuxFilterMainType::MMTP;
-    filterLinkageTypes[SOURCE][1].subType.mmtpFilterType(DemuxMmtpFilterType::AUDIO);
-    filterLinkageTypes[SINK][1] = filterLinkageTypes[SOURCE][1];
-    // IP Linkage filter setting
-    filterLinkageTypes[SOURCE][2].mainType = DemuxFilterMainType::IP;
-    filterLinkageTypes[SOURCE][2].subType.ipFilterType(DemuxIpFilterType::IP);
-    filterLinkageTypes[SINK][2] = filterLinkageTypes[SOURCE][2];
-    // TLV Linkage filter setting
-    filterLinkageTypes[SOURCE][3].mainType = DemuxFilterMainType::TLV;
-    filterLinkageTypes[SOURCE][3].subType.tlvFilterType(DemuxTlvFilterType::TLV);
-    filterLinkageTypes[SINK][3] = filterLinkageTypes[SOURCE][3];
-    // ALP Linkage PTP filter setting
-    filterLinkageTypes[SOURCE][4].mainType = DemuxFilterMainType::ALP;
-    filterLinkageTypes[SOURCE][4].subType.alpFilterType(DemuxAlpFilterType::PTP);
-    filterLinkageTypes[SINK][4] = filterLinkageTypes[SOURCE][4];
+    filterMap[defaultVideoFilterId].type.mainType = DemuxFilterMainType::TS;
+    filterMap[defaultVideoFilterId].type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+    filterMap[defaultVideoFilterId].bufferSize = FMQ_SIZE_16M;
+    filterMap[defaultVideoFilterId].settings.ts().tpid = 256;
+    filterMap[defaultVideoFilterId].settings.ts().filterSettings.av({.isPassthrough = false});
+
+    filterMap[defaultAudioFilterId].type.mainType = DemuxFilterMainType::TS;
+    filterMap[defaultAudioFilterId].type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
+    filterMap[defaultAudioFilterId].bufferSize = FMQ_SIZE_16M;
+    filterMap[defaultAudioFilterId].settings.ts().tpid = 256;
+    filterMap[defaultAudioFilterId].settings.ts().filterSettings.av({.isPassthrough = false});
+
+    // Read customized config
+    TunerTestingConfigReader::readFilterConfig1_0(filterMap);
 };
 
-/** Configuration array for the timer filter test */
-inline void initTimeFilterConfig() {
-    timeFilterArray[TIMER0].supportTimeFilter = true;
-    timeFilterArray[TIMER0].timeStamp = 1;
-}
-
-/** Configuration array for the dvr test */
+/** Config all the dvrs that would be used in the tests */
 inline void initDvrConfig() {
-    RecordSettings recordSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::TS,
-            .packetSize = 188,
-    };
-    dvrArray[DVR_RECORD0].type = DvrType::RECORD;
-    dvrArray[DVR_RECORD0].bufferSize = FMQ_SIZE_4M;
-    dvrArray[DVR_RECORD0].settings.record(recordSettings);
-    PlaybackSettings playbackSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::TS,
-            .packetSize = 188,
-    };
-    dvrArray[DVR_PLAYBACK0].type = DvrType::PLAYBACK;
-    dvrArray[DVR_PLAYBACK0].playbackInputFile = "/data/local/tmp/segment000000.ts";
-    dvrArray[DVR_PLAYBACK0].bufferSize = FMQ_SIZE_4M;
-    dvrArray[DVR_PLAYBACK0].settings.playback(playbackSettings);
-    PlaybackSettings softwareFePlaybackSettings{
-            .statusMask = 0xf,
-            .lowThreshold = 0x1000,
-            .highThreshold = 0x07fff,
-            .dataFormat = DataFormat::TS,
-            .packetSize = 188,
-    };
-    dvrArray[DVR_SOFTWARE_FE].type = DvrType::PLAYBACK;
-    dvrArray[DVR_SOFTWARE_FE].playbackInputFile = "/data/local/tmp/segment000000.ts";
-    dvrArray[DVR_SOFTWARE_FE].bufferSize = FMQ_SIZE_4M;
-    dvrArray[DVR_SOFTWARE_FE].settings.playback(softwareFePlaybackSettings);
+    // Read customized config
+    TunerTestingConfigReader::readDvrConfig1_0(dvrMap);
 };
 
-/** Configuration array for the descrambler test */
-inline void initDescramblerConfig() {
-    descramblerArray[DESC_0].casSystemId = CLEAR_KEY_SYSTEM_ID;
-    descramblerArray[DESC_0].provisionStr = PROVISION_STR;
-    descramblerArray[DESC_0].hidlPvtData.resize(256);
+/** Config all the lnbs that would be used in the tests */
+inline void initLnbConfig() {
+    // Read customized config
+    TunerTestingConfigReader::readLnbConfig1_0(lnbMap);
+    TunerTestingConfigReader::readDiseqcMessages(diseqcMsgMap);
 };
+
+/** Config all the time filters that would be used in the tests */
+inline void initTimeFilterConfig() {
+    // Read customized config
+    TunerTestingConfigReader::readTimeFilterConfig1_0(timeFilterMap);
+};
+
+/** Config all the descramblers that would be used in the tests */
+inline void initDescramblerConfig() {
+    // Read customized config
+    TunerTestingConfigReader::readDescramblerConfig1_0(descramblerMap);
+};
+
+/** Read the vendor configurations of which hardware to use for each test cases/data flows */
+inline void connectHardwaresToTestCases() {
+    TunerTestingConfigReader::connectLiveBroadcast(live);
+    TunerTestingConfigReader::connectScan(scan);
+    TunerTestingConfigReader::connectDvrPlayback(playback);
+    TunerTestingConfigReader::connectDvrRecord(record);
+    TunerTestingConfigReader::connectDescrambling(descrambling);
+    TunerTestingConfigReader::connectLnbLive(lnbLive);
+    TunerTestingConfigReader::connectLnbRecord(lnbRecord);
+    TunerTestingConfigReader::connectTimeFilter(timeFilter);
+};
+
+inline bool validateConnections() {
+    if ((!live.hasFrontendConnection || !scan.hasFrontendConnection) && !playback.support) {
+        ALOGW("[vts config] VTS must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    if (record.support && !record.hasFrontendConnection &&
+        record.dvrSourceId.compare(emptyHardwareId) == 0) {
+        ALOGW("[vts config] Record must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    if (descrambling.support && !descrambling.hasFrontendConnection &&
+        descrambling.dvrSourceId.compare(emptyHardwareId) == 0) {
+        ALOGW("[vts config] Descrambling must support either a DVR source or a Frontend source.");
+        return false;
+    }
+
+    bool feIsValid = live.hasFrontendConnection
+                             ? frontendMap.find(live.frontendId) != frontendMap.end()
+                             : true;
+    feIsValid &= scan.hasFrontendConnection ? frontendMap.find(scan.frontendId) != frontendMap.end()
+                                            : true;
+    feIsValid &= record.support && record.hasFrontendConnection
+                         ? frontendMap.find(record.frontendId) != frontendMap.end()
+                         : true;
+    feIsValid &= (descrambling.support && descrambling.hasFrontendConnection)
+                         ? frontendMap.find(descrambling.frontendId) != frontendMap.end()
+                         : true;
+    feIsValid &= lnbLive.support ? frontendMap.find(lnbLive.frontendId) != frontendMap.end() : true;
+    feIsValid &=
+            lnbRecord.support ? frontendMap.find(lnbRecord.frontendId) != frontendMap.end() : true;
+
+    if (!feIsValid) {
+        ALOGW("[vts config] dynamic config fe connection is invalid.");
+        return false;
+    }
+
+    bool dvrIsValid = (live.hasFrontendConnection && frontendMap[live.frontendId].isSoftwareFe)
+                              ? dvrMap.find(live.dvrSoftwareFeId) != dvrMap.end()
+                              : true;
+    dvrIsValid &= playback.support ? dvrMap.find(playback.dvrId) != dvrMap.end() : true;
+    if (record.support) {
+        if (record.hasFrontendConnection) {
+            if (frontendMap[record.frontendId].isSoftwareFe) {
+                dvrIsValid &= dvrMap.find(record.dvrSoftwareFeId) != dvrMap.end();
+            }
+        } else {
+            dvrIsValid &= dvrMap.find(record.dvrSourceId) != dvrMap.end();
+        }
+        dvrIsValid &= dvrMap.find(record.dvrRecordId) != dvrMap.end();
+    }
+    if (descrambling.support) {
+        if (descrambling.hasFrontendConnection) {
+            if (frontendMap[descrambling.frontendId].isSoftwareFe) {
+                dvrIsValid &= dvrMap.find(descrambling.dvrSoftwareFeId) != dvrMap.end();
+            }
+        } else {
+            dvrIsValid &= dvrMap.find(descrambling.dvrSourceId) != dvrMap.end();
+        }
+    }
+
+    if (!dvrIsValid) {
+        ALOGW("[vts config] dynamic config dvr connection is invalid.");
+        return false;
+    }
+
+    bool filterIsValid = (live.hasFrontendConnection)
+                                 ? filterMap.find(live.audioFilterId) != filterMap.end() &&
+                                           filterMap.find(live.videoFilterId) != filterMap.end()
+                                 : true;
+    filterIsValid &= playback.support
+                             ? (filterMap.find(playback.audioFilterId) != filterMap.end() &&
+                                filterMap.find(playback.videoFilterId) != filterMap.end())
+                             : true;
+    filterIsValid &=
+            record.support ? filterMap.find(record.recordFilterId) != filterMap.end() : true;
+    filterIsValid &= descrambling.support
+                             ? (filterMap.find(descrambling.audioFilterId) != filterMap.end() &&
+                                filterMap.find(descrambling.videoFilterId) != filterMap.end())
+                             : true;
+    filterIsValid &= lnbLive.support ? (filterMap.find(lnbLive.audioFilterId) != filterMap.end() &&
+                                        filterMap.find(lnbLive.videoFilterId) != filterMap.end())
+                                     : true;
+    filterIsValid &=
+            lnbRecord.support ? filterMap.find(lnbRecord.recordFilterId) != filterMap.end() : true;
+
+    if (!filterIsValid) {
+        ALOGW("[vts config] dynamic config filter connection is invalid.");
+        return false;
+    }
+
+    bool lnbIsValid = lnbLive.support ? lnbMap.find(lnbLive.lnbId) != lnbMap.end() : true;
+    lnbIsValid &= lnbRecord.support ? lnbMap.find(lnbRecord.lnbId) != lnbMap.end() : true;
+
+    if (!lnbIsValid) {
+        ALOGW("[vts config] dynamic config lnb connection is invalid.");
+        return false;
+    }
+
+    bool descramblerIsValid =
+            descrambling.support
+                    ? descramblerMap.find(descrambling.descramblerId) != descramblerMap.end()
+                    : true;
+
+    if (!descramblerIsValid) {
+        ALOGW("[vts config] dynamic config descrambler connection is invalid.");
+        return false;
+    }
+
+    bool diseqcMsgIsValid = true;
+    if (lnbLive.support) {
+        for (auto msgName : lnbLive.diseqcMsgs) {
+            diseqcMsgIsValid &= diseqcMsgMap.find(msgName) != diseqcMsgMap.end();
+        }
+    }
+    if (lnbRecord.support) {
+        for (auto msgName : lnbRecord.diseqcMsgs) {
+            diseqcMsgIsValid &= diseqcMsgMap.find(msgName) != diseqcMsgMap.end();
+        }
+    }
+
+    if (!diseqcMsgIsValid) {
+        ALOGW("[vts config] dynamic config diseqcMsg sender is invalid.");
+        return false;
+    }
+
+    return true;
+}
diff --git a/tv/tuner/README.md b/tv/tuner/README.md
index aa1f62d..2a358f5 100644
--- a/tv/tuner/README.md
+++ b/tv/tuner/README.md
@@ -5,8 +5,3 @@
 TV specific tuners.
 
 See 1.0/ITuner.hal for an overview.
-
-*** note
-**Warning:** The HALs are not (yet) frozen, as the HAL definition is
-expected to evolve between Android releases.
-***
diff --git a/tv/tuner/assets/Android.bp b/tv/tuner/assets/Android.bp
new file mode 100644
index 0000000..79244ed
--- /dev/null
+++ b/tv/tuner/assets/Android.bp
@@ -0,0 +1,26 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+genrule {
+    name: "tuner_frontend_input_es",
+    srcs: ["tuner_frontend_input.es"],
+    out: [
+        "test.es",
+    ],
+    cmd: "cp -f $(in) $(genDir)/test.es",
+}
+
+genrule {
+    name: "tuner_frontend_input_ts",
+    srcs: ["tuner_frontend_input.ts"],
+    out: [
+        "segment000000.ts",
+    ],
+    cmd: "cp -f $(in) $(genDir)/segment000000.ts",
+}
diff --git a/tv/tuner/assets/tuner_frontend_input.es b/tv/tuner/assets/tuner_frontend_input.es
new file mode 100644
index 0000000..b53c957
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.es
Binary files differ
diff --git a/tv/tuner/assets/tuner_frontend_input.ts b/tv/tuner/assets/tuner_frontend_input.ts
new file mode 100644
index 0000000..8992c7b
--- /dev/null
+++ b/tv/tuner/assets/tuner_frontend_input.ts
Binary files differ
diff --git a/tv/tuner/config/Android.bp b/tv/tuner/config/Android.bp
new file mode 100644
index 0000000..ddbf3a7
--- /dev/null
+++ b/tv/tuner/config/Android.bp
@@ -0,0 +1,31 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0_enums",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+    enums_only: true,
+}
+
+xsd_config {
+    name: "tuner_testing_dynamic_configuration_V1_0_parser",
+    srcs: ["tuner_testing_dynamic_configuration.xsd"],
+    package_name: "android.media.tuner.testing.configuration.V1_0",
+    nullability: true,
+    parser_only: true,
+}
diff --git a/tv/cec/2.0/default/OWNERS b/tv/tuner/config/OWNERS
similarity index 100%
rename from tv/cec/2.0/default/OWNERS
rename to tv/tuner/config/OWNERS
diff --git a/tv/tuner/config/TunerTestingConfigReader.h b/tv/tuner/config/TunerTestingConfigReader.h
new file mode 100644
index 0000000..e1f5695
--- /dev/null
+++ b/tv/tuner/config/TunerTestingConfigReader.h
@@ -0,0 +1,789 @@
+/*
+ * Copyright 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.
+ */
+
+#include <android-base/logging.h>
+#include <android/hardware/tv/tuner/1.0/types.h>
+#include <android_media_tuner_testing_configuration_V1_0.h>
+#include <android_media_tuner_testing_configuration_V1_0_enums.h>
+#include <binder/MemoryDealer.h>
+#include <hidl/HidlSupport.h>
+#include <hidl/HidlTransportSupport.h>
+#include <hidl/Status.h>
+#include <hidlmemory/FrameworkUtils.h>
+
+using namespace std;
+using namespace android::media::tuner::testing::configuration::V1_0;
+
+using android::hardware::tv::tuner::V1_0::DataFormat;
+using android::hardware::tv::tuner::V1_0::DemuxAlpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterAvSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterEvent;
+using android::hardware::tv::tuner::V1_0::DemuxFilterMainType;
+using android::hardware::tv::tuner::V1_0::DemuxFilterRecordSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSectionSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterSettings;
+using android::hardware::tv::tuner::V1_0::DemuxFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxIpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxMmtpFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxRecordScIndexType;
+using android::hardware::tv::tuner::V1_0::DemuxTlvFilterType;
+using android::hardware::tv::tuner::V1_0::DemuxTpid;
+using android::hardware::tv::tuner::V1_0::DemuxTsFilterType;
+using android::hardware::tv::tuner::V1_0::DvrSettings;
+using android::hardware::tv::tuner::V1_0::DvrType;
+using android::hardware::tv::tuner::V1_0::FrontendDvbsSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtBandwidth;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtCoderate;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtConstellation;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtGuardInterval;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtHierarchy;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtPlpMode;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtSettings;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtStandard;
+using android::hardware::tv::tuner::V1_0::FrontendDvbtTransmissionMode;
+using android::hardware::tv::tuner::V1_0::FrontendSettings;
+using android::hardware::tv::tuner::V1_0::FrontendStatus;
+using android::hardware::tv::tuner::V1_0::FrontendStatusType;
+using android::hardware::tv::tuner::V1_0::FrontendType;
+using android::hardware::tv::tuner::V1_0::LnbPosition;
+using android::hardware::tv::tuner::V1_0::LnbTone;
+using android::hardware::tv::tuner::V1_0::LnbVoltage;
+using android::hardware::tv::tuner::V1_0::PlaybackSettings;
+using android::hardware::tv::tuner::V1_0::RecordSettings;
+
+const string configFilePath = "/vendor/etc/tuner_vts_config.xml";
+const string emptyHardwareId = "";
+
+#define PROVISION_STR                                      \
+    "{                                                   " \
+    "  \"id\": 21140844,                                 " \
+    "  \"name\": \"Test Title\",                         " \
+    "  \"lowercase_organization_name\": \"Android\",     " \
+    "  \"asset_key\": {                                  " \
+    "  \"encryption_key\": \"nezAr3CHFrmBR9R8Tedotw==\"  " \
+    "  },                                                " \
+    "  \"cas_type\": 1,                                  " \
+    "  \"track_types\": [ ]                              " \
+    "}                                                   "
+
+struct FrontendConfig {
+    bool isSoftwareFe;
+    FrontendType type;
+    FrontendSettings settings;
+    vector<FrontendStatusType> tuneStatusTypes;
+    vector<FrontendStatus> expectTuneStatuses;
+};
+
+struct FilterConfig {
+    uint32_t bufferSize;
+    DemuxFilterType type;
+    DemuxFilterSettings settings;
+    bool getMqDesc;
+
+    bool operator<(const FilterConfig& /*c*/) const { return false; }
+};
+
+struct DvrConfig {
+    DvrType type;
+    uint32_t bufferSize;
+    DvrSettings settings;
+    string playbackInputFile;
+};
+
+struct LnbConfig {
+    string name;
+    LnbVoltage voltage;
+    LnbTone tone;
+    LnbPosition position;
+};
+
+struct TimeFilterConfig {
+    uint64_t timeStamp;
+};
+
+struct DescramblerConfig {
+    uint32_t casSystemId;
+    string provisionStr;
+    vector<uint8_t> hidlPvtData;
+};
+
+struct LiveBroadcastHardwareConnections {
+    bool hasFrontendConnection;
+    string frontendId;
+    string dvrSoftwareFeId;
+    string audioFilterId;
+    string videoFilterId;
+    string sectionFilterId;
+    string pcrFilterId;
+    /* list string of extra filters; */
+};
+
+struct ScanHardwareConnections {
+    bool hasFrontendConnection;
+    string frontendId;
+};
+
+struct DvrPlaybackHardwareConnections {
+    bool support;
+    string frontendId;
+    string dvrId;
+    string audioFilterId;
+    string videoFilterId;
+    string sectionFilterId;
+    /* list string of extra filters; */
+};
+
+struct DvrRecordHardwareConnections {
+    bool support;
+    bool hasFrontendConnection;
+    string frontendId;
+    string dvrRecordId;
+    string dvrSoftwareFeId;
+    string recordFilterId;
+    string dvrSourceId;
+};
+
+struct DescramblingHardwareConnections {
+    bool support;
+    bool hasFrontendConnection;
+    string frontendId;
+    string dvrSoftwareFeId;
+    string audioFilterId;
+    string videoFilterId;
+    string descramblerId;
+    string dvrSourceId;
+    /* list string of extra filters; */
+};
+
+struct LnbLiveHardwareConnections {
+    bool support;
+    string frontendId;
+    string audioFilterId;
+    string videoFilterId;
+    string lnbId;
+    vector<string> diseqcMsgs;
+    /* list string of extra filters; */
+};
+
+struct LnbRecordHardwareConnections {
+    bool support;
+    string frontendId;
+    string dvrRecordId;
+    string recordFilterId;
+    string lnbId;
+    vector<string> diseqcMsgs;
+    /* list string of extra filters; */
+};
+
+struct TimeFilterHardwareConnections {
+    bool support;
+    string timeFilterId;
+};
+
+struct TunerTestingConfigReader {
+  public:
+    static bool checkConfigFileExists() {
+        auto res = read(configFilePath.c_str());
+        if (res == nullopt) {
+            ALOGW("[ConfigReader] Couldn't read /vendor/etc/tuner_vts_config.xml."
+                  "Please check tuner_testing_dynamic_configuration.xsd"
+                  "and sample_tuner_vts_config.xml for more details on how to config Tune VTS.");
+        }
+        return (res != nullopt);
+    }
+
+    static void readFrontendConfig1_0(map<string, FrontendConfig>& frontendMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasFrontends()) {
+            // TODO: b/182519645 complete the tune status config
+            vector<FrontendStatusType> types;
+            types.push_back(FrontendStatusType::DEMOD_LOCK);
+            FrontendStatus status;
+            status.isDemodLocked(true);
+            vector<FrontendStatus> statuses;
+            statuses.push_back(status);
+
+            auto frontends = *hardwareConfig.getFirstFrontends();
+            for (auto feConfig : frontends.getFrontend()) {
+                string id = feConfig.getId();
+                if (id.compare(string("FE_DEFAULT")) == 0) {
+                    // overrid default
+                    frontendMap.erase(string("FE_DEFAULT"));
+                }
+                FrontendType type;
+                switch (feConfig.getType()) {
+                    case FrontendTypeEnum::UNDEFINED:
+                        type = FrontendType::UNDEFINED;
+                        break;
+                    // TODO: b/182519645 finish all other frontend settings
+                    case FrontendTypeEnum::ANALOG:
+                        type = FrontendType::ANALOG;
+                        break;
+                    case FrontendTypeEnum::ATSC:
+                        type = FrontendType::ATSC;
+                        break;
+                    case FrontendTypeEnum::ATSC3:
+                        type = FrontendType::ATSC3;
+                        break;
+                    case FrontendTypeEnum::DVBC:
+                        type = FrontendType::DVBC;
+                        break;
+                    case FrontendTypeEnum::DVBS:
+                        type = FrontendType::DVBS;
+                        frontendMap[id].settings.dvbs(readDvbsFrontendSettings(feConfig));
+                        break;
+                    case FrontendTypeEnum::DVBT: {
+                        type = FrontendType::DVBT;
+                        frontendMap[id].settings.dvbt(readDvbtFrontendSettings(feConfig));
+                        break;
+                    }
+                    case FrontendTypeEnum::ISDBS:
+                        type = FrontendType::ISDBS;
+                        break;
+                    case FrontendTypeEnum::ISDBS3:
+                        type = FrontendType::ISDBS3;
+                        break;
+                    case FrontendTypeEnum::ISDBT:
+                        type = FrontendType::ISDBT;
+                        break;
+                    case FrontendTypeEnum::DTMB:
+                        // dtmb will be handled in readFrontendConfig1_1;
+                        continue;
+                    case FrontendTypeEnum::UNKNOWN:
+                        ALOGW("[ConfigReader] invalid frontend type");
+                        return;
+                }
+                frontendMap[id].type = type;
+                frontendMap[id].isSoftwareFe = feConfig.getIsSoftwareFrontend();
+                // TODO: b/182519645 complete the tune status config
+                frontendMap[id].tuneStatusTypes = types;
+                frontendMap[id].expectTuneStatuses = statuses;
+            }
+        }
+    }
+
+    static void readFilterConfig1_0(map<string, FilterConfig>& filterMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasFilters()) {
+            auto filters = *hardwareConfig.getFirstFilters();
+            for (auto filterConfig : filters.getFilter()) {
+                string id = filterConfig.getId();
+                if (id.compare(string("FILTER_AUDIO_DEFAULT")) == 0) {
+                    // overrid default
+                    filterMap.erase(string("FILTER_AUDIO_DEFAULT"));
+                }
+                if (id.compare(string("FILTER_VIDEO_DEFAULT")) == 0) {
+                    // overrid default
+                    filterMap.erase(string("FILTER_VIDEO_DEFAULT"));
+                }
+
+                DemuxFilterType type;
+                DemuxFilterSettings settings;
+                if (!readFilterTypeAndSettings(filterConfig, type, settings)) {
+                    ALOGW("[ConfigReader] invalid filter type");
+                    return;
+                }
+                filterMap[id].type = type;
+                filterMap[id].bufferSize = filterConfig.getBufferSize();
+                filterMap[id].getMqDesc = filterConfig.getUseFMQ();
+                filterMap[id].settings = settings;
+            }
+        }
+    }
+
+    static void readDvrConfig1_0(map<string, DvrConfig>& dvrMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasDvrs()) {
+            auto dvrs = *hardwareConfig.getFirstDvrs();
+            for (auto dvrConfig : dvrs.getDvr()) {
+                string id = dvrConfig.getId();
+                DvrType type;
+                switch (dvrConfig.getType()) {
+                    case DvrTypeEnum::PLAYBACK:
+                        type = DvrType::PLAYBACK;
+                        dvrMap[id].settings.playback(readPlaybackSettings(dvrConfig));
+                        break;
+                    case DvrTypeEnum::RECORD:
+                        type = DvrType::RECORD;
+                        dvrMap[id].settings.record(readRecordSettings(dvrConfig));
+                        break;
+                    case DvrTypeEnum::UNKNOWN:
+                        ALOGW("[ConfigReader] invalid DVR type");
+                        return;
+                }
+                dvrMap[id].type = type;
+                dvrMap[id].bufferSize = static_cast<uint32_t>(dvrConfig.getBufferSize());
+                if (dvrConfig.hasInputFilePath()) {
+                    dvrMap[id].playbackInputFile = dvrConfig.getInputFilePath();
+                }
+            }
+        }
+    }
+
+    static void readLnbConfig1_0(map<string, LnbConfig>& lnbMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasLnbs()) {
+            auto lnbs = *hardwareConfig.getFirstLnbs();
+            for (auto lnbConfig : lnbs.getLnb()) {
+                string id = lnbConfig.getId();
+                if (lnbConfig.hasName()) {
+                    lnbMap[id].name = lnbConfig.getName();
+                } else {
+                    lnbMap[id].name = emptyHardwareId;
+                }
+                lnbMap[id].voltage = static_cast<LnbVoltage>(lnbConfig.getVoltage());
+                lnbMap[id].tone = static_cast<LnbTone>(lnbConfig.getTone());
+                lnbMap[id].position = static_cast<LnbPosition>(lnbConfig.getPosition());
+            }
+        }
+    }
+
+    static void readDescramblerConfig1_0(map<string, DescramblerConfig>& descramblerMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasDescramblers()) {
+            auto descramblers = *hardwareConfig.getFirstDescramblers();
+            for (auto descramblerConfig : descramblers.getDescrambler()) {
+                string id = descramblerConfig.getId();
+                descramblerMap[id].casSystemId =
+                        static_cast<uint32_t>(descramblerConfig.getCasSystemId());
+                if (descramblerConfig.hasProvisionStr()) {
+                    descramblerMap[id].provisionStr = descramblerConfig.getProvisionStr();
+                } else {
+                    descramblerMap[id].provisionStr = PROVISION_STR;
+                }
+                if (descramblerConfig.hasSesstionPrivatData()) {
+                    auto privateData = descramblerConfig.getSesstionPrivatData();
+                    int size = privateData.size();
+                    descramblerMap[id].hidlPvtData.resize(size);
+                    memcpy(descramblerMap[id].hidlPvtData.data(), privateData.data(), size);
+                }
+            }
+        }
+    }
+
+    static void readDiseqcMessages(map<string, vector<uint8_t>>& diseqcMsgMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasDiseqcMessages()) {
+            auto msgs = *hardwareConfig.getFirstDiseqcMessages();
+            for (auto msgConfig : msgs.getDiseqcMessage()) {
+                string name = msgConfig.getMsgName();
+                for (uint8_t atom : msgConfig.getMsgBody()) {
+                    diseqcMsgMap[name].push_back(atom);
+                }
+            }
+        }
+    }
+
+    static void readTimeFilterConfig1_0(map<string, TimeFilterConfig>& timeFilterMap) {
+        auto hardwareConfig = getHardwareConfig();
+        if (hardwareConfig.hasTimeFilters()) {
+            auto timeFilters = *hardwareConfig.getFirstTimeFilters();
+            for (auto timeFilterConfig : timeFilters.getTimeFilter()) {
+                string id = timeFilterConfig.getId();
+                timeFilterMap[id].timeStamp =
+                        static_cast<uint64_t>(timeFilterConfig.getTimeStamp());
+            }
+        }
+    }
+
+    static void connectLiveBroadcast(LiveBroadcastHardwareConnections& live) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasClearLiveBroadcast()) {
+            live.hasFrontendConnection = true;
+        } else {
+            live.hasFrontendConnection = false;
+            return;
+        }
+        auto liveConfig = *dataFlow.getFirstClearLiveBroadcast();
+        live.frontendId = liveConfig.getFrontendConnection();
+
+        live.audioFilterId = liveConfig.getAudioFilterConnection();
+        live.videoFilterId = liveConfig.getVideoFilterConnection();
+        if (liveConfig.hasPcrFilterConnection()) {
+            live.pcrFilterId = liveConfig.getPcrFilterConnection();
+        } else {
+            live.pcrFilterId = emptyHardwareId;
+        }
+        if (liveConfig.hasSectionFilterConnection()) {
+            live.sectionFilterId = liveConfig.getSectionFilterConnection();
+        } else {
+            live.sectionFilterId = emptyHardwareId;
+        }
+        if (liveConfig.hasDvrSoftwareFeConnection()) {
+            live.dvrSoftwareFeId = liveConfig.getDvrSoftwareFeConnection();
+        }
+    }
+
+    static void connectScan(ScanHardwareConnections& scan) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasScan()) {
+            scan.hasFrontendConnection = true;
+        } else {
+            scan.hasFrontendConnection = false;
+            return;
+        }
+        auto scanConfig = *dataFlow.getFirstScan();
+        scan.frontendId = scanConfig.getFrontendConnection();
+    }
+
+    static void connectDvrPlayback(DvrPlaybackHardwareConnections& playback) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasDvrPlayback()) {
+            playback.support = true;
+        } else {
+            playback.support = false;
+            return;
+        }
+        auto playbackConfig = *dataFlow.getFirstDvrPlayback();
+        playback.dvrId = playbackConfig.getDvrConnection();
+        playback.audioFilterId = playbackConfig.getAudioFilterConnection();
+        playback.videoFilterId = playbackConfig.getVideoFilterConnection();
+        if (playbackConfig.hasSectionFilterConnection()) {
+            playback.sectionFilterId = playbackConfig.getSectionFilterConnection();
+        } else {
+            playback.sectionFilterId = emptyHardwareId;
+        }
+    }
+
+    static void connectDvrRecord(DvrRecordHardwareConnections& record) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasDvrRecord()) {
+            record.support = true;
+        } else {
+            record.support = false;
+            return;
+        }
+        auto recordConfig = *dataFlow.getFirstDvrRecord();
+        record.recordFilterId = recordConfig.getRecordFilterConnection();
+        record.dvrRecordId = recordConfig.getDvrRecordConnection();
+        if (recordConfig.hasDvrSoftwareFeConnection()) {
+            record.dvrSoftwareFeId = recordConfig.getDvrSoftwareFeConnection();
+        }
+        if (recordConfig.getHasFrontendConnection()) {
+            record.hasFrontendConnection = true;
+            record.dvrSourceId = emptyHardwareId;
+            record.frontendId = recordConfig.getFrontendConnection();
+        } else {
+            record.hasFrontendConnection = false;
+            record.dvrSourceId = recordConfig.getDvrSourceConnection();
+        }
+    }
+
+    static void connectDescrambling(DescramblingHardwareConnections& descrambling) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasDescrambling()) {
+            descrambling.support = true;
+        } else {
+            descrambling.support = false;
+            return;
+        }
+        auto descConfig = *dataFlow.getFirstDescrambling();
+        descrambling.descramblerId = descConfig.getDescramblerConnection();
+        descrambling.audioFilterId = descConfig.getAudioFilterConnection();
+        descrambling.videoFilterId = descConfig.getVideoFilterConnection();
+        if (descConfig.hasDvrSoftwareFeConnection()) {
+            descrambling.dvrSoftwareFeId = descConfig.getDvrSoftwareFeConnection();
+        }
+        if (descConfig.getHasFrontendConnection()) {
+            descrambling.hasFrontendConnection = true;
+            descrambling.dvrSourceId = emptyHardwareId;
+            descrambling.frontendId = descConfig.getFrontendConnection();
+        } else {
+            descrambling.hasFrontendConnection = false;
+            descrambling.dvrSourceId = descConfig.getDvrSourceConnection();
+        }
+    }
+
+    static void connectLnbLive(LnbLiveHardwareConnections& lnbLive) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasLnbLive()) {
+            lnbLive.support = true;
+        } else {
+            lnbLive.support = false;
+            return;
+        }
+        auto lnbLiveConfig = *dataFlow.getFirstLnbLive();
+        lnbLive.frontendId = lnbLiveConfig.getFrontendConnection();
+        lnbLive.audioFilterId = lnbLiveConfig.getAudioFilterConnection();
+        lnbLive.videoFilterId = lnbLiveConfig.getVideoFilterConnection();
+        lnbLive.lnbId = lnbLiveConfig.getLnbConnection();
+        if (lnbLiveConfig.hasDiseqcMsgSender()) {
+            for (auto msgName : lnbLiveConfig.getDiseqcMsgSender()) {
+                lnbLive.diseqcMsgs.push_back(msgName);
+            }
+        }
+    }
+
+    static void connectLnbRecord(LnbRecordHardwareConnections& lnbRecord) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasLnbRecord()) {
+            lnbRecord.support = true;
+        } else {
+            lnbRecord.support = false;
+            return;
+        }
+        auto lnbRecordConfig = *dataFlow.getFirstLnbRecord();
+        lnbRecord.frontendId = lnbRecordConfig.getFrontendConnection();
+        lnbRecord.recordFilterId = lnbRecordConfig.getRecordFilterConnection();
+        lnbRecord.dvrRecordId = lnbRecordConfig.getDvrRecordConnection();
+        lnbRecord.lnbId = lnbRecordConfig.getLnbConnection();
+        if (lnbRecordConfig.hasDiseqcMsgSender()) {
+            for (auto msgName : lnbRecordConfig.getDiseqcMsgSender()) {
+                lnbRecord.diseqcMsgs.push_back(msgName);
+            }
+        }
+    }
+
+    static void connectTimeFilter(TimeFilterHardwareConnections& timeFilter) {
+        auto dataFlow = getDataFlowConfiguration();
+        if (dataFlow.hasTimeFilter()) {
+            timeFilter.support = true;
+        } else {
+            timeFilter.support = false;
+            return;
+        }
+        auto timeFilterConfig = *dataFlow.getFirstTimeFilter();
+        timeFilter.timeFilterId = timeFilterConfig.getTimeFilterConnection();
+    }
+
+  private:
+    static FrontendDvbtSettings readDvbtFrontendSettings(Frontend feConfig) {
+        ALOGW("[ConfigReader] fe type is dvbt");
+        FrontendDvbtSettings dvbtSettings{
+                .frequency = (uint32_t)feConfig.getFrequency(),
+        };
+        if (!feConfig.hasDvbtFrontendSettings_optional()) {
+            ALOGW("[ConfigReader] no more dvbt settings");
+            return dvbtSettings;
+        }
+        dvbtSettings.transmissionMode = static_cast<FrontendDvbtTransmissionMode>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getTransmissionMode());
+        dvbtSettings.bandwidth = static_cast<FrontendDvbtBandwidth>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getBandwidth());
+        dvbtSettings.constellation = static_cast<FrontendDvbtConstellation>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getConstellation());
+        dvbtSettings.hierarchy = static_cast<FrontendDvbtHierarchy>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getHierarchy());
+        dvbtSettings.hpCoderate = static_cast<FrontendDvbtCoderate>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getHpCoderate());
+        dvbtSettings.lpCoderate = static_cast<FrontendDvbtCoderate>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getLpCoderate());
+        dvbtSettings.guardInterval = static_cast<FrontendDvbtGuardInterval>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getGuardInterval());
+        dvbtSettings.isHighPriority =
+                feConfig.getFirstDvbtFrontendSettings_optional()->getIsHighPriority();
+        dvbtSettings.standard = static_cast<FrontendDvbtStandard>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getStandard());
+        dvbtSettings.isMiso = feConfig.getFirstDvbtFrontendSettings_optional()->getIsMiso();
+        dvbtSettings.plpMode = static_cast<FrontendDvbtPlpMode>(
+                feConfig.getFirstDvbtFrontendSettings_optional()->getPlpMode());
+        dvbtSettings.plpId = feConfig.getFirstDvbtFrontendSettings_optional()->getPlpId();
+        dvbtSettings.plpGroupId = feConfig.getFirstDvbtFrontendSettings_optional()->getPlpGroupId();
+
+        return dvbtSettings;
+    }
+
+    static FrontendDvbsSettings readDvbsFrontendSettings(Frontend feConfig) {
+        ALOGW("[ConfigReader] fe type is dvbs");
+        FrontendDvbsSettings dvbsSettings{
+                .frequency = (uint32_t)feConfig.getFrequency(),
+        };
+        if (!feConfig.hasDvbsFrontendSettings_optional()) {
+            ALOGW("[ConfigReader] no more dvbs settings");
+            return dvbsSettings;
+        }
+        dvbsSettings.symbolRate = static_cast<uint32_t>(
+                feConfig.getFirstDvbsFrontendSettings_optional()->getSymbolRate());
+        dvbsSettings.inputStreamId = static_cast<uint32_t>(
+                feConfig.getFirstDvbsFrontendSettings_optional()->getInputStreamId());
+        return dvbsSettings;
+    }
+
+    static bool readFilterTypeAndSettings(Filter filterConfig, DemuxFilterType& type,
+                                          DemuxFilterSettings& settings) {
+        auto mainType = filterConfig.getMainType();
+        auto subType = filterConfig.getSubType();
+        uint32_t pid = static_cast<uint32_t>(filterConfig.getPid());
+        switch (mainType) {
+            case FilterMainTypeEnum::TS: {
+                ALOGW("[ConfigReader] filter main type is ts");
+                type.mainType = DemuxFilterMainType::TS;
+                switch (subType) {
+                    case FilterSubTypeEnum::UNDEFINED:
+                        break;
+                    case FilterSubTypeEnum::SECTION:
+                        type.subType.tsFilterType(DemuxTsFilterType::SECTION);
+                        settings.ts().filterSettings.section(
+                                readSectionFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::PES:
+                        // TODO: b/182519645 support all the filter settings
+                        /*settings.ts().filterSettings.pesData(
+                                getPesFilterSettings(filterConfig));*/
+                        type.subType.tsFilterType(DemuxTsFilterType::PES);
+                        break;
+                    case FilterSubTypeEnum::TS:
+                        type.subType.tsFilterType(DemuxTsFilterType::TS);
+                        settings.ts().filterSettings.noinit();
+                        break;
+                    case FilterSubTypeEnum::PCR:
+                        type.subType.tsFilterType(DemuxTsFilterType::PCR);
+                        settings.ts().filterSettings.noinit();
+                        break;
+                    case FilterSubTypeEnum::TEMI:
+                        type.subType.tsFilterType(DemuxTsFilterType::TEMI);
+                        settings.ts().filterSettings.noinit();
+                        break;
+                    case FilterSubTypeEnum::AUDIO:
+                        type.subType.tsFilterType(DemuxTsFilterType::AUDIO);
+                        settings.ts().filterSettings.av(readAvFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::VIDEO:
+                        type.subType.tsFilterType(DemuxTsFilterType::VIDEO);
+                        settings.ts().filterSettings.av(readAvFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::RECORD:
+                        type.subType.tsFilterType(DemuxTsFilterType::RECORD);
+                        settings.ts().filterSettings.record(readRecordFilterSettings(filterConfig));
+                        break;
+                    default:
+                        ALOGW("[ConfigReader] ts subtype is not supported");
+                        return false;
+                }
+                settings.ts().tpid = pid;
+                break;
+            }
+            case FilterMainTypeEnum::MMTP: {
+                ALOGW("[ConfigReader] filter main type is mmtp");
+                type.mainType = DemuxFilterMainType::MMTP;
+                switch (subType) {
+                    case FilterSubTypeEnum::UNDEFINED:
+                        break;
+                    case FilterSubTypeEnum::SECTION:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::SECTION);
+                        settings.mmtp().filterSettings.section(
+                                readSectionFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::PES:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::PES);
+                        // TODO: b/182519645 support all the filter settings
+                        /*settings.mmtp().filterSettings.pesData(
+                                getPesFilterSettings(filterConfig));*/
+                        break;
+                    case FilterSubTypeEnum::MMTP:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::MMTP);
+                        settings.mmtp().filterSettings.noinit();
+                        break;
+                    case FilterSubTypeEnum::AUDIO:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::AUDIO);
+                        settings.mmtp().filterSettings.av(readAvFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::VIDEO:
+                        settings.mmtp().filterSettings.av(readAvFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::RECORD:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::RECORD);
+                        settings.mmtp().filterSettings.record(
+                                readRecordFilterSettings(filterConfig));
+                        break;
+                    case FilterSubTypeEnum::DOWNLOAD:
+                        type.subType.mmtpFilterType(DemuxMmtpFilterType::DOWNLOAD);
+                        // TODO: b/182519645 support all the filter settings
+                        /*settings.mmtp().filterSettings.download(
+                                getDownloadFilterSettings(filterConfig));*/
+                        break;
+                    default:
+                        ALOGW("[ConfigReader] mmtp subtype is not supported");
+                        return false;
+                }
+                settings.mmtp().mmtpPid = pid;
+                break;
+            }
+            default:
+                // TODO: b/182519645 support all the filter configs
+                ALOGW("[ConfigReader] filter main type is not supported in dynamic config");
+                return false;
+        }
+        return true;
+    }
+
+    static DemuxFilterSectionSettings readSectionFilterSettings(Filter filterConfig) {
+        DemuxFilterSectionSettings settings;
+        if (!filterConfig.hasSectionFilterSettings_optional()) {
+            return settings;
+        }
+        auto section = filterConfig.getFirstSectionFilterSettings_optional();
+        settings.isCheckCrc = section->getIsCheckCrc();
+        settings.isRepeat = section->getIsRepeat();
+        settings.isRaw = section->getIsRaw();
+        return settings;
+    }
+
+    static DemuxFilterAvSettings readAvFilterSettings(Filter filterConfig) {
+        DemuxFilterAvSettings settings;
+        if (!filterConfig.hasAvFilterSettings_optional()) {
+            return settings;
+        }
+        auto av = filterConfig.getFirstAvFilterSettings_optional();
+        settings.isPassthrough = av->getIsPassthrough();
+        return settings;
+    }
+
+    static DemuxFilterRecordSettings readRecordFilterSettings(Filter filterConfig) {
+        DemuxFilterRecordSettings settings;
+        if (!filterConfig.hasRecordFilterSettings_optional()) {
+            return settings;
+        }
+        auto record = filterConfig.getFirstRecordFilterSettings_optional();
+        settings.tsIndexMask = static_cast<uint32_t>(record->getTsIndexMask());
+        settings.scIndexType = static_cast<DemuxRecordScIndexType>(record->getScIndexType());
+        return settings;
+    }
+
+    static PlaybackSettings readPlaybackSettings(Dvr dvrConfig) {
+        ALOGW("[ConfigReader] dvr type is playback");
+        PlaybackSettings playbackSettings{
+                .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+                .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+                .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+                .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+                .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+        };
+        return playbackSettings;
+    }
+
+    static RecordSettings readRecordSettings(Dvr dvrConfig) {
+        ALOGW("[ConfigReader] dvr type is record");
+        RecordSettings recordSettings{
+                .statusMask = static_cast<uint8_t>(dvrConfig.getStatusMask()),
+                .lowThreshold = static_cast<uint32_t>(dvrConfig.getLowThreshold()),
+                .highThreshold = static_cast<uint32_t>(dvrConfig.getHighThreshold()),
+                .dataFormat = static_cast<DataFormat>(dvrConfig.getDataFormat()),
+                .packetSize = static_cast<uint8_t>(dvrConfig.getPacketSize()),
+        };
+        return recordSettings;
+    }
+
+    static TunerConfiguration getTunerConfig() { return *read(configFilePath.c_str()); }
+
+    static HardwareConfiguration getHardwareConfig() {
+        return *getTunerConfig().getFirstHardwareConfiguration();
+    }
+
+    static DataFlowConfiguration getDataFlowConfiguration() {
+        return *getTunerConfig().getFirstDataFlowConfiguration();
+    }
+};
diff --git a/tv/tuner/config/api/current.txt b/tv/tuner/config/api/current.txt
new file mode 100644
index 0000000..3476414
--- /dev/null
+++ b/tv/tuner/config/api/current.txt
@@ -0,0 +1,461 @@
+// Signature format: 2.0
+package android.media.tuner.testing.configuration.V1_0 {
+
+  public class AvFilterSettings {
+    ctor public AvFilterSettings();
+    method @Nullable public boolean getIsPassthrough();
+    method public void setIsPassthrough(@Nullable boolean);
+  }
+
+  public class DataFlowConfiguration {
+    ctor public DataFlowConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast getClearLiveBroadcast();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling getDescrambling();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback getDvrPlayback();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord getDvrRecord();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive getLnbLive();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord getLnbRecord();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan getScan();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.TimeFilter getTimeFilter();
+    method public void setClearLiveBroadcast(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.ClearLiveBroadcast);
+    method public void setDescrambling(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Descrambling);
+    method public void setDvrPlayback(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrPlayback);
+    method public void setDvrRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.DvrRecord);
+    method public void setLnbLive(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbLive);
+    method public void setLnbRecord(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.LnbRecord);
+    method public void setScan(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.Scan);
+    method public void setTimeFilter(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration.TimeFilter);
+  }
+
+  public static class DataFlowConfiguration.ClearLiveBroadcast {
+    ctor public DataFlowConfiguration.ClearLiveBroadcast();
+    method @Nullable public String getAudioFilterConnection();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public String getPcrFilterConnection();
+    method @Nullable public String getSectionFilterConnection();
+    method @Nullable public String getVideoFilterConnection();
+    method public void setAudioFilterConnection(@Nullable String);
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setPcrFilterConnection(@Nullable String);
+    method public void setSectionFilterConnection(@Nullable String);
+    method public void setVideoFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.Descrambling {
+    ctor public DataFlowConfiguration.Descrambling();
+    method @Nullable public String getAudioFilterConnection();
+    method @Nullable public String getDescramblerConnection();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getDvrSourceConnection();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public boolean getHasFrontendConnection();
+    method @Nullable public String getVideoFilterConnection();
+    method public void setAudioFilterConnection(@Nullable String);
+    method public void setDescramblerConnection(@Nullable String);
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setDvrSourceConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setHasFrontendConnection(@Nullable boolean);
+    method public void setVideoFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.DvrPlayback {
+    ctor public DataFlowConfiguration.DvrPlayback();
+    method @Nullable public String getAudioFilterConnection();
+    method @Nullable public String getDvrConnection();
+    method @Nullable public String getSectionFilterConnection();
+    method @Nullable public String getVideoFilterConnection();
+    method public void setAudioFilterConnection(@Nullable String);
+    method public void setDvrConnection(@Nullable String);
+    method public void setSectionFilterConnection(@Nullable String);
+    method public void setVideoFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.DvrRecord {
+    ctor public DataFlowConfiguration.DvrRecord();
+    method @Nullable public String getDvrRecordConnection();
+    method @Nullable public String getDvrSoftwareFeConnection();
+    method @Nullable public String getDvrSourceConnection();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public boolean getHasFrontendConnection();
+    method @Nullable public String getRecordFilterConnection();
+    method public void setDvrRecordConnection(@Nullable String);
+    method public void setDvrSoftwareFeConnection(@Nullable String);
+    method public void setDvrSourceConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setHasFrontendConnection(@Nullable boolean);
+    method public void setRecordFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.LnbLive {
+    ctor public DataFlowConfiguration.LnbLive();
+    method @Nullable public String getAudioFilterConnection();
+    method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public String getLnbConnection();
+    method @Nullable public String getVideoFilterConnection();
+    method public void setAudioFilterConnection(@Nullable String);
+    method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setLnbConnection(@Nullable String);
+    method public void setVideoFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.LnbRecord {
+    ctor public DataFlowConfiguration.LnbRecord();
+    method @Nullable public java.util.List<java.lang.String> getDiseqcMsgSender();
+    method @Nullable public String getDvrRecordConnection();
+    method @Nullable public String getFrontendConnection();
+    method @Nullable public String getLnbConnection();
+    method @Nullable public String getRecordFilterConnection();
+    method public void setDiseqcMsgSender(@Nullable java.util.List<java.lang.String>);
+    method public void setDvrRecordConnection(@Nullable String);
+    method public void setFrontendConnection(@Nullable String);
+    method public void setLnbConnection(@Nullable String);
+    method public void setRecordFilterConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.Scan {
+    ctor public DataFlowConfiguration.Scan();
+    method @Nullable public String getFrontendConnection();
+    method public void setFrontendConnection(@Nullable String);
+  }
+
+  public static class DataFlowConfiguration.TimeFilter {
+    ctor public DataFlowConfiguration.TimeFilter();
+    method @Nullable public String getTimeFilterConnection();
+    method public void setTimeFilterConnection(@Nullable String);
+  }
+
+  public class Descrambler {
+    ctor public Descrambler();
+    method @Nullable public java.math.BigInteger getCasSystemId();
+    method @Nullable public String getId();
+    method @Nullable public String getProvisionStr();
+    method @Nullable public java.util.List<java.lang.Short> getSesstionPrivatData();
+    method public void setCasSystemId(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setProvisionStr(@Nullable String);
+    method public void setSesstionPrivatData(@Nullable java.util.List<java.lang.Short>);
+  }
+
+  public class DiseqcMessage {
+    ctor public DiseqcMessage();
+    method @Nullable public java.util.List<java.lang.Short> getMsgBody();
+    method @Nullable public String getMsgName();
+    method public void setMsgBody(@Nullable java.util.List<java.lang.Short>);
+    method public void setMsgName(@Nullable String);
+  }
+
+  public class DvbsFrontendSettings {
+    ctor public DvbsFrontendSettings();
+    method @Nullable public java.math.BigInteger getInputStreamId();
+    method @Nullable public java.math.BigInteger getSymbolRate();
+    method public void setInputStreamId(@Nullable java.math.BigInteger);
+    method public void setSymbolRate(@Nullable java.math.BigInteger);
+  }
+
+  public class DvbtFrontendSettings {
+    ctor public DvbtFrontendSettings();
+    method @Nullable public java.math.BigInteger getBandwidth();
+    method @Nullable public java.math.BigInteger getConstellation();
+    method @Nullable public java.math.BigInteger getGuardInterval();
+    method @Nullable public java.math.BigInteger getHierarchy();
+    method @Nullable public java.math.BigInteger getHpCoderate();
+    method @Nullable public java.math.BigInteger getIsHighPriority();
+    method @Nullable public java.math.BigInteger getIsMiso();
+    method @Nullable public java.math.BigInteger getLpCoderate();
+    method @Nullable public java.math.BigInteger getPlpGroupId();
+    method @Nullable public java.math.BigInteger getPlpId();
+    method @Nullable public java.math.BigInteger getPlpMode();
+    method @Nullable public java.math.BigInteger getStandard();
+    method @Nullable public java.math.BigInteger getTransmissionMode();
+    method public void setBandwidth(@Nullable java.math.BigInteger);
+    method public void setConstellation(@Nullable java.math.BigInteger);
+    method public void setGuardInterval(@Nullable java.math.BigInteger);
+    method public void setHierarchy(@Nullable java.math.BigInteger);
+    method public void setHpCoderate(@Nullable java.math.BigInteger);
+    method public void setIsHighPriority(@Nullable java.math.BigInteger);
+    method public void setIsMiso(@Nullable java.math.BigInteger);
+    method public void setLpCoderate(@Nullable java.math.BigInteger);
+    method public void setPlpGroupId(@Nullable java.math.BigInteger);
+    method public void setPlpId(@Nullable java.math.BigInteger);
+    method public void setPlpMode(@Nullable java.math.BigInteger);
+    method public void setStandard(@Nullable java.math.BigInteger);
+    method public void setTransmissionMode(@Nullable java.math.BigInteger);
+  }
+
+  public class Dvr {
+    ctor public Dvr();
+    method @Nullable public java.math.BigInteger getBufferSize();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum getDataFormat();
+    method @Nullable public java.math.BigInteger getHighThreshold();
+    method @Nullable public String getId();
+    method @Nullable public String getInputFilePath();
+    method @Nullable public java.math.BigInteger getLowThreshold();
+    method @Nullable public java.math.BigInteger getPacketSize();
+    method @Nullable public java.math.BigInteger getStatusMask();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvrTypeEnum getType();
+    method public void setBufferSize(@Nullable java.math.BigInteger);
+    method public void setDataFormat(@Nullable android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum);
+    method public void setHighThreshold(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setInputFilePath(@Nullable String);
+    method public void setLowThreshold(@Nullable java.math.BigInteger);
+    method public void setPacketSize(@Nullable java.math.BigInteger);
+    method public void setStatusMask(@Nullable java.math.BigInteger);
+    method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.DvrTypeEnum);
+  }
+
+  public enum DvrDataFormatEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum ES;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum PES;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum SHV_TLV;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrDataFormatEnum TS;
+  }
+
+  public enum DvrStatusEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum DATA_READY;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum HIGH_WATER;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum LOW_WATER;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrStatusEnum OVERFLOW;
+  }
+
+  public enum DvrTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum PLAYBACK;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.DvrTypeEnum RECORD;
+  }
+
+  public class Filter {
+    ctor public Filter();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.AvFilterSettings getAvFilterSettings_optional();
+    method @Nullable public java.math.BigInteger getBufferSize();
+    method @Nullable public String getId();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum getMainType();
+    method @Nullable public java.math.BigInteger getPid();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.RecordFilterSettings getRecordFilterSettings_optional();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.SectionFilterSettings getSectionFilterSettings_optional();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum getSubType();
+    method @Nullable public boolean getUseFMQ();
+    method public void setAvFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.AvFilterSettings);
+    method public void setBufferSize(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setMainType(@Nullable android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum);
+    method public void setPid(@Nullable java.math.BigInteger);
+    method public void setRecordFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.RecordFilterSettings);
+    method public void setSectionFilterSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.SectionFilterSettings);
+    method public void setSubType(@Nullable android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum);
+    method public void setUseFMQ(@Nullable boolean);
+  }
+
+  public enum FilterMainTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum MMTP;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterMainTypeEnum TS;
+  }
+
+  public enum FilterSubTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum AUDIO;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum DOWNLOAD;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum MMTP;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum PCR;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum PES;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum RECORD;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum SECTION;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum TEMI;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum TS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum UNDEFINED;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FilterSubTypeEnum VIDEO;
+  }
+
+  public class Frontend {
+    ctor public Frontend();
+    method @Nullable public java.math.BigInteger getConnectToCicamId();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings getDvbsFrontendSettings_optional();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings getDvbtFrontendSettings_optional();
+    method @Nullable public java.math.BigInteger getEndFrequency();
+    method @Nullable public java.math.BigInteger getFrequency();
+    method @Nullable public String getId();
+    method @Nullable public boolean getIsSoftwareFrontend();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum getType();
+    method public void setConnectToCicamId(@Nullable java.math.BigInteger);
+    method public void setDvbsFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbsFrontendSettings);
+    method public void setDvbtFrontendSettings_optional(@Nullable android.media.tuner.testing.configuration.V1_0.DvbtFrontendSettings);
+    method public void setEndFrequency(@Nullable java.math.BigInteger);
+    method public void setFrequency(@Nullable java.math.BigInteger);
+    method public void setId(@Nullable String);
+    method public void setIsSoftwareFrontend(@Nullable boolean);
+    method public void setType(@Nullable android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum);
+  }
+
+  public enum FrontendTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ANALOG;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ATSC3;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DTMB;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBC;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum DVBT;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBS3;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum ISDBT;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.FrontendTypeEnum UNDEFINED;
+  }
+
+  public class HardwareConfiguration {
+    ctor public HardwareConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Descramblers getDescramblers();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.DiseqcMessages getDiseqcMessages();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs getDvrs();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Filters getFilters();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends getFrontends();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Lnbs getLnbs();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.TimeFilters getTimeFilters();
+    method public void setDescramblers(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Descramblers);
+    method public void setDiseqcMessages(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.DiseqcMessages);
+    method public void setDvrs(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Dvrs);
+    method public void setFilters(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Filters);
+    method public void setFrontends(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Frontends);
+    method public void setLnbs(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.Lnbs);
+    method public void setTimeFilters(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration.TimeFilters);
+  }
+
+  public static class HardwareConfiguration.Descramblers {
+    ctor public HardwareConfiguration.Descramblers();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Descrambler> getDescrambler();
+  }
+
+  public static class HardwareConfiguration.DiseqcMessages {
+    ctor public HardwareConfiguration.DiseqcMessages();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.DiseqcMessage> getDiseqcMessage();
+  }
+
+  public static class HardwareConfiguration.Dvrs {
+    ctor public HardwareConfiguration.Dvrs();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Dvr> getDvr();
+  }
+
+  public static class HardwareConfiguration.Filters {
+    ctor public HardwareConfiguration.Filters();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Filter> getFilter();
+  }
+
+  public static class HardwareConfiguration.Frontends {
+    ctor public HardwareConfiguration.Frontends();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Frontend> getFrontend();
+  }
+
+  public static class HardwareConfiguration.Lnbs {
+    ctor public HardwareConfiguration.Lnbs();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.Lnb> getLnb();
+  }
+
+  public static class HardwareConfiguration.TimeFilters {
+    ctor public HardwareConfiguration.TimeFilters();
+    method @Nullable public java.util.List<android.media.tuner.testing.configuration.V1_0.TimeFilter> getTimeFilter();
+  }
+
+  public class Lnb {
+    ctor public Lnb();
+    method @Nullable public String getId();
+    method @Nullable public String getName();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbPositionEnum getPosition();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbToneEnum getTone();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum getVoltage();
+    method public void setId(@Nullable String);
+    method public void setName(@Nullable String);
+    method public void setPosition(@Nullable android.media.tuner.testing.configuration.V1_0.LnbPositionEnum);
+    method public void setTone(@Nullable android.media.tuner.testing.configuration.V1_0.LnbToneEnum);
+    method public void setVoltage(@Nullable android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum);
+  }
+
+  public enum LnbPositionEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum POSITION_A;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum POSITION_B;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbPositionEnum UNDEFINED;
+  }
+
+  public enum LnbToneEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbToneEnum CONTINUOUS;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbToneEnum NONE;
+  }
+
+  public enum LnbVoltageEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum NONE;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_11V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_12V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_13V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_14V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_15V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_18V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_19V;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.LnbVoltageEnum VOLTAGE_5V;
+  }
+
+  public class RecordFilterSettings {
+    ctor public RecordFilterSettings();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum getScIndexType();
+    method @Nullable public java.math.BigInteger getTsIndexMask();
+    method public void setScIndexType(@Nullable android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum);
+    method public void setTsIndexMask(@Nullable java.math.BigInteger);
+  }
+
+  public enum ScIndexTypeEnum {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum NONE;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum SC;
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.ScIndexTypeEnum SC_HEVC;
+  }
+
+  public class SectionFilterSettings {
+    ctor public SectionFilterSettings();
+    method @Nullable public boolean getIsCheckCrc();
+    method @Nullable public boolean getIsRaw();
+    method @Nullable public boolean getIsRepeat();
+    method public void setIsCheckCrc(@Nullable boolean);
+    method public void setIsRaw(@Nullable boolean);
+    method public void setIsRepeat(@Nullable boolean);
+  }
+
+  public class TimeFilter {
+    ctor public TimeFilter();
+    method @Nullable public String getId();
+    method @Nullable public java.math.BigInteger getTimeStamp();
+    method public void setId(@Nullable String);
+    method public void setTimeStamp(@Nullable java.math.BigInteger);
+  }
+
+  public class TunerConfiguration {
+    ctor public TunerConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration getDataFlowConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.HardwareConfiguration getHardwareConfiguration();
+    method @Nullable public android.media.tuner.testing.configuration.V1_0.Version getVersion();
+    method public void setDataFlowConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.DataFlowConfiguration);
+    method public void setHardwareConfiguration(@Nullable android.media.tuner.testing.configuration.V1_0.HardwareConfiguration);
+    method public void setVersion(@Nullable android.media.tuner.testing.configuration.V1_0.Version);
+  }
+
+  public enum Version {
+    method @NonNull public String getRawName();
+    enum_constant public static final android.media.tuner.testing.configuration.V1_0.Version _1_0;
+  }
+
+  public class XmlParser {
+    ctor public XmlParser();
+    method @Nullable public static android.media.tuner.testing.configuration.V1_0.TunerConfiguration read(@NonNull java.io.InputStream) throws javax.xml.datatype.DatatypeConfigurationException, java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method @Nullable public static String readText(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+    method public static void skip(@NonNull org.xmlpull.v1.XmlPullParser) throws java.io.IOException, org.xmlpull.v1.XmlPullParserException;
+  }
+
+}
+
diff --git a/tests/bar/1.0/.hidl_for_test b/tv/tuner/config/api/last_current.txt
similarity index 100%
rename from tests/bar/1.0/.hidl_for_test
rename to tv/tuner/config/api/last_current.txt
diff --git a/tests/bar/1.0/.hidl_for_test b/tv/tuner/config/api/last_removed.txt
similarity index 100%
copy from tests/bar/1.0/.hidl_for_test
copy to tv/tuner/config/api/last_removed.txt
diff --git a/tv/tuner/config/api/removed.txt b/tv/tuner/config/api/removed.txt
new file mode 100644
index 0000000..d802177
--- /dev/null
+++ b/tv/tuner/config/api/removed.txt
@@ -0,0 +1 @@
+// Signature format: 2.0
diff --git a/tv/tuner/config/sample_tuner_vts_config.xml b/tv/tuner/config/sample_tuner_vts_config.xml
new file mode 100644
index 0000000..347e984
--- /dev/null
+++ b/tv/tuner/config/sample_tuner_vts_config.xml
@@ -0,0 +1,229 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<!-- 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.
+-->
+
+<!-- The Sample Tuner Testing Configuration.
+    Name the customized xml with "tuner_vts_config.xml" and push into the device
+    "/vendor/etc" path. Please use "tuner_testing_dynamic_configuration.xsd" to verify the xml.
+    The version section contains a “version” tag in the form “major.minor” e.g version=”1.0”
+    This shows the tuner dynamic configuration version. -->
+<TunerConfiguration version="1.0" xmlns:xi="http://www.w3.org/2001/XInclude">
+    <!-- Hardware Configuration section contains the configurations of all the hardwares
+        that would be used in the tests. In the "dataFlowConfiguration" section, each data flow
+        under test has its required/optional hardwares. The ids configured in the
+        "dataFlowConfiguration" would be used to connect the hardware to each data flow test. -->
+    <hardwareConfiguration>
+        <!-- Frontends section:
+            This section contains configurations of all the frontends that would be used
+                in the tests.
+                - This section is optional and can be skipped to use the default fe settings.
+                - The default settings can be found in the sample_tuner_vts_configurations.xml.
+                - The users can also override the default frontend settings using id="FE_DEFAULT".
+                - The users can configure 1 or more frontend elements in the frontends sections.
+
+            Each frontend element contain the following attributes:
+                "id": unique id of the frontend that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "type": the frontend type. The enums are defined in the xsd.
+                "isSoftwareFrontend": if the test environment is using hardware or software
+                    frontend. If using software, a ts input file path needs to be configured.
+                "softwareFeInputPath": used as the source of the software frontend.
+                "connectToCicamId": if the device supports frontend connecting to cicam, the target
+                    cicam id needs to be configured here. Supported in Tuner 1.1 or higher.
+                "frequency": the frequency used to configure tune and scan.
+                "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+            Each frontend element also contains one and only one type-related "frontendSettings".
+                - The settings type should match the frontend "type" attribute.
+                - For example, when frontend type="DVBT", dvbtFrontendSettings can be configured.
+                - This is optional and skipping the settings would pass a setting with frequency
+                    config only to the hal.
+        -->
+        <frontends>
+            <frontend id="FE_DEFAULT" type="DVBT" isSoftwareFrontend="true"
+                      connectToCicamId="0" frequency="578000" endFrequency="800000">
+                <dvbtFrontendSettings bandwidth="8" transmissionMode="1" isHighPriority="1"
+                                      constellation="1" hierarchy="1" hpCoderate="1" lpCoderate="1"
+                                      guardInterval="1" standard="1" isMiso="0" plpMode="1"
+                                      plpId="0" plpGroupId="0"/>
+            </frontend>
+            <frontend id="FE_DVBS_0" type="DVBS" isSoftwareFrontend="true"
+                      connectToCicamId="0" frequency="578000" endFrequency="800000">
+            </frontend>
+        </frontends>
+        <!-- Filter section:
+            This section contains configurations of all the filters that would be used in the tests.
+                - This section is optional and can be skipped to use the default filter settings.
+                - The default settings can be found in the sample_tuner_vts_configurations.xml.
+                - The users can also override the default filter settings using
+                - id="FILTER_AUDIO_DEFAULT" or "FILTER_VIDEO_DEFAULT".
+                - The users can configure 1 or more filter elements in the filters sections.
+
+            Each filter element contain the following attributes:
+                "id": unique id of the filter that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "mainType": the main filter type. The enums are defined in the xsd.
+                "subType": the sub filter type. The enums are defined in the xsd.
+                "bufferSize": the buffer size of the filter in hex.
+                "pid": the pid that would be used to configure the filter.
+                "useFMQ": if the filter uses FMQ.
+
+            Each filter element also contains at most one type-related "filterSettings".
+                - The settings type should match the filter "subType" attribute.
+                - For example, when filter subType is audio or video, the avFilterSettings can be
+                    configured.
+                - This is optional and skipping the settings would pass a setting with tpid config
+                    only to the hal.
+        -->
+        <filters>
+            <filter id="FILTER_AUDIO_DEFAULT" mainType="TS" subType="AUDIO"
+                    bufferSize="16777216" pid="257" useFMQ="false">
+                <avFilterSettings isPassthrough="false"/>
+            </filter>
+            <filter id="FILTER_VIDEO_DEFAULT" mainType="TS" subType="VIDEO"
+                    bufferSize="16777216" pid="256" useFMQ="false">
+                <avFilterSettings isPassthrough="false"/>
+            </filter>
+            <filter id="FILTER_TS_RECORD_0" mainType="TS" subType="RECORD"
+                    bufferSize="16777216" pid="257" useFMQ="false">
+                <recordFilterSettings tsIndexMask="1" scIndexType="NONE"/>
+            </filter>
+            <filter id="FILTER_TS_SECTION_0" mainType="TS" subType="SECTION"
+                    bufferSize="16777216" pid="257" useFMQ="true">
+                <sectionFilterSettings isCheckCrc="false" isRepeat="false" isRaw="false"/>
+            </filter>
+            <filter id="FILTER_TS_PCR_0" mainType="TS" subType="PCR"
+                    bufferSize="16777216" pid="256" useFMQ="false">
+            </filter>
+        </filters>
+        <!-- Dvr section:
+            This section contains configurations of all the dvrs that would be used in the tests.
+                - This section is optional and can be skipped if DVR is not supported.
+                - The users can configure 1 or more dvr elements in the dvrs sections.
+
+            Each dvr element contain the following attributes:
+                "id": unique id of the dvr that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "type": the dvr type.
+                "bufferSize": the dvr buffer size.
+                "statusMask": register callbacks of specific status.
+                "lowThreshold": the dvr status low threshold.
+                "highThreshold": the dvr status high threshold.
+                "dataFormat": the dvr data format.
+                "packetSize": the dvr packet size.
+                "inputFilePath": the dvr playback input file path. Only required in playback dvr.
+        -->
+        <dvrs>
+            <dvr id="DVR_PLAYBACK_0" type="PLAYBACK" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="TS" packetSize="188" inputFilePath="/data/local/tmp/segment000000.ts"/>
+            <dvr id="DVR_RECORD_0" type="RECORD" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="TS" packetSize="188"/>
+            <dvr id="DVR_PLAYBACK_1" type="PLAYBACK" bufferSize="4194304"
+                 statusMask="15" lowThreshold="4096" highThreshold="32767"
+                 dataFormat="ES" packetSize="188" inputFilePath="/data/local/tmp/test.es"/>
+        </dvrs>
+        <!-- Lnb section:
+            This section contains configurations of all the lnbs that would be used in the tests.
+                - This section is optional and can be skipped if LNB is not supported.
+                - The users can configure 1 or more lnb elements in the lnbs sections.
+
+            Each lnb element contain the following attributes:
+                "id": unique id of the lnb that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "name": the external lnb device name.
+                "voltage": the voltage used to config the lnb.
+                "tone": the voltage used to config the lnb.
+                "position": the voltage used to config the lnb.
+        -->
+        <diseqcMessages>
+            <diseqcMessage msgName="DISEQC_POWER_ON" msgBody="14 0 0 0 0 3"/>
+        </diseqcMessages>
+        <lnbs>
+            <lnb id="LNB_0" voltage="VOLTAGE_12V" tone="NONE" position="UNDEFINED"/>
+            <lnb id="LNB_1" name="default_lnb_external" voltage="VOLTAGE_5V"
+                            tone="NONE" position="UNDEFINED"/>
+        </lnbs>
+        <!-- TimeFilter section:
+            This section contains configurations of all the time filters that would be used in
+            the tests.
+                - This section is optional and can be skipped if Time Filter is not supported.
+                - The users can configure 1 or more timeFilter elements in the timeFilters sections.
+
+            Each timeFilter element contain the following attributes:
+                "id": unique id of the time filter that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "timeStamp": the time stamp used to config the time filter.
+        -->
+        <timeFilters>
+            <timeFilter id="TIME_FILTER_0" timeStamp="1"/>
+        </timeFilters>
+        <!-- Descrambler section:
+            This section contains configurations of all the descramblers that would be used in
+            the tests.
+                - This section is optional and can be skipped if Descrambler is not supported.
+                - The users can configure 1 or more descrambler elements in the descramblers sections.
+
+            Each Descrambler element contain the following attributes:
+                "id": unique id of the descrambler that could be used to connect to the test the
+                    "dataFlowConfiguration"
+                "casSystemId": the cas system id to connect to the descrambler.
+                "provisionStr": the provision string to use with the cas plugin.
+                "sesstionPrivatData": the session private data used to open the cas session.
+        -->
+        <descramblers>
+            <descrambler id="DESCRAMBLER_0" casSystemId="63192"/>
+        </descramblers>
+    </hardwareConfiguration>
+
+    <!-- Data flow configuration section connects each data flow under test to the ids of the
+        hardwares that would be used during the tests. -->
+    <dataFlowConfiguration>
+        <clearLiveBroadcast frontendConnection="FE_DEFAULT"
+                            audioFilterConnection="FILTER_AUDIO_DEFAULT"
+                            videoFilterConnection="FILTER_VIDEO_DEFAULT"
+                            pcrFilterConnection="FILTER_TS_PCR_0"
+                            sectionFilterConnection="FILTER_TS_SECTION_0"
+                            dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <scan frontendConnection="FE_DEFAULT"/>
+        <descrambling hasFrontendConnection="true"
+                      frontendConnection="FE_DEFAULT"
+                      descramblerConnection="DESCRAMBLER_0"
+                      audioFilterConnection="FILTER_AUDIO_DEFAULT"
+                      videoFilterConnection="FILTER_VIDEO_DEFAULT"
+                      dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <dvrPlayback dvrConnection="DVR_PLAYBACK_0"
+                     audioFilterConnection="FILTER_AUDIO_DEFAULT"
+                     videoFilterConnection="FILTER_VIDEO_DEFAULT"
+                     sectionFilterConnection="FILTER_TS_SECTION_0"/>
+        <dvrRecord hasFrontendConnection="true"
+                   frontendConnection="FE_DEFAULT"
+                   recordFilterConnection="FILTER_TS_RECORD_0"
+                   dvrRecordConnection="DVR_RECORD_0"
+                   dvrSoftwareFeConnection="DVR_PLAYBACK_0"/>
+        <lnbLive frontendConnection="FE_DVBS_0"
+                 audioFilterConnection="FILTER_AUDIO_DEFAULT"
+                 videoFilterConnection="FILTER_VIDEO_DEFAULT"
+                 lnbConnection="LNB_1"
+                 diseqcMsgSender="DISEQC_POWER_ON"/>
+        <lnbRecord frontendConnection="FE_DVBS_0"
+                   recordFilterConnection="FILTER_TS_RECORD_0"
+                   dvrRecordConnection="DVR_RECORD_0"
+                   lnbConnection="LNB_0"
+                   diseqcMsgSender="DISEQC_POWER_ON"/>
+        <timeFilter timeFilterConnection="TIME_FILTER_0"/>
+    </dataFlowConfiguration>
+</TunerConfiguration>
diff --git a/tv/tuner/config/tuner_testing_dynamic_configuration.xsd b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
new file mode 100644
index 0000000..7f31a11
--- /dev/null
+++ b/tv/tuner/config/tuner_testing_dynamic_configuration.xsd
@@ -0,0 +1,668 @@
+<?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.
+-->
+<xs:schema version="2.0"
+           elementFormDefault="qualified"
+           attributeFormDefault="unqualified"
+           xmlns:xs="http://www.w3.org/2001/XMLSchema">
+    <!-- List the dynamic config versions supported by tuner testing. -->
+    <xs:simpleType name="version">
+        <xs:restriction base="xs:decimal">
+            <xs:enumeration value="1.0"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!-- FRONTEND SESSION -->
+    <xs:simpleType name="frontendId">
+        <!-- Frontend id must be either FE_DEFAULT or FE_TYPE_NUM
+            <frontend id="FE_DVBS_0"/>
+        -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="FE_DEFAULT|FE_[A-Z]+_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="frontendTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="UNDEFINED" />
+            <xs:enumeration value="ANALOG" />
+            <xs:enumeration value="ATSC" />
+            <xs:enumeration value="ATSC3"/>
+            <xs:enumeration value="DVBC"/>
+            <xs:enumeration value="DVBS"/>
+            <xs:enumeration value="DVBT"/>
+            <xs:enumeration value="ISDBS"/>
+            <xs:enumeration value="ISDBS3"/>
+            <xs:enumeration value="ISDBT"/>
+            <xs:enumeration value="DTMB"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="dvbtFrontendSettings">
+        <xs:attribute name="bandwidth" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="constellation" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="guardInterval" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="hierarchy" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="hpCoderate" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="lpCoderate" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="isHighPriority" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="isMiso" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpGroupId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="plpMode" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="standard" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="transmissionMode" type="xs:nonNegativeInteger" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="dvbsFrontendSettings">
+        <xs:attribute name="inputStreamId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="symbolRate" type="xs:nonNegativeInteger" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="frontend">
+        <xs:annotation>
+            <xs:documentation>
+                Each frontend element contain the following attributes:
+                    "id": unique id of the frontend that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "type": the frontend type. The enums are defined in the xsd.
+                    "isSoftwareFrontend": if the test environment is using hardware or software
+                        frontend. If using software, a ts input file path needs to be configured.
+                    "softwareFeInputPath": used as the source of the software frontend.
+                    "connectToCicamId": if the device supports frontend connecting to cicam, the
+                        target cicam id needs to be configured here. Supported in Tuner 1.1 or
+                        higher.
+                    "frequency": the frequency used to configure tune and scan.
+                    "endFrequency": the end frequency of scan. Supported in Tuner 1.1 or higher.
+
+                Each frontend element also contains at most one type-related "frontendSettings".
+                    - The settings type should match the frontend "type" attribute.
+                    - For example, when frontend type="DVBT", dvbtFrontendSettings can be
+                        configured.
+                    - This is optional and skipping the settings would pass a setting with frequency
+                        config only to the hal.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="1">
+            <!-- TODO: b/182519645 finish all the frontend settings structures. -->
+            <!--xs:element name="analog" type="analogSettings"/>
+            <xs:element name="atsc" type="atscSettings"/>
+            <xs:element name="atsc3" type="atsc3Settings"/>
+            <xs:element name="dvbc" type="dvbcSettings"/-->
+            <xs:element name="dvbsFrontendSettings" type="dvbsFrontendSettings"/>
+            <xs:element name="dvbtFrontendSettings" type="dvbtFrontendSettings"/>
+            <!--xs:element name="isdbs" type="isdbsSettings"/>
+            <xs:element name="isdbs3" type="isdbs3Settings"/>
+            <xs:element name="isdbt" type="isdbtSettings"/>
+            <xs:element name="dtmb" type="dtmbSettings"/-->
+        </xs:choice>
+        <xs:attribute name="id" type="frontendId" use="required"/>
+        <xs:attribute name="type" type="frontendTypeEnum" use="required"/>
+        <!-- A dvr connection is required in the data flow config section when
+            "isSoftwareFrontend" is true. -->
+        <xs:attribute name="isSoftwareFrontend" type="xs:boolean" use="required"/>
+        <xs:attribute name="frequency" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="connectToCicamId" type="xs:nonNegativeInteger" use="optional"/>
+        <xs:attribute name="endFrequency" type="xs:nonNegativeInteger" use="optional"/>
+    </xs:complexType>
+
+    <!-- FILTER SESSION -->
+    <xs:simpleType name="filterId">
+        <!-- Filter id must be either FILTER_AUDIO_DEFAULT or FILTER_VIDEO_DEFAULT
+             or FILTER_MAINTYPE_SUBTYPE_NUM
+            <filter id="FILTER_TS_AUDIO_0"/>
+        -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="FILTER_AUDIO_DEFAULT|FILTER_VIDEO_DEFAULT|FILTER_[A-Z]+_[A-Z]+_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!-- A list of filter ids that could be used in the data flow configurations to connect
+        filters under testing. -->
+    <xs:simpleType name="filterConnections">
+        <xs:list itemType="filterId" />
+    </xs:simpleType>
+    <!-- DemuxFilterRecordSettings::tsIndexMask -->
+    <xs:simpleType name="tsIndexMask">
+        <xs:restriction base="xs:integer">
+            <xs:minInclusive value="0"/>
+            <xs:maxInclusive value="8191"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <!-- DemuxFilterRecordSettings::scIndexType -->
+    <xs:simpleType name="scIndexTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="NONE" />
+            <xs:enumeration value="SC" />
+            <xs:enumeration value="SC_HEVC"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="filterMainTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="TS" />
+            <xs:enumeration value="MMTP" />
+            <!-- TODO: b/182519645 Support IP/TLV/ALP filter config
+            <xs:enumeration value="IP"/>
+            <xs:enumeration value="TLV"/>
+            <xs:enumeration value="ALP"/-->
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="filterSubTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="UNDEFINED" />
+            <xs:enumeration value="SECTION" />
+            <xs:enumeration value="PES" />
+            <xs:enumeration value="TS"/>
+            <xs:enumeration value="AUDIO"/>
+            <xs:enumeration value="VIDEO"/>
+            <xs:enumeration value="PCR"/>
+            <xs:enumeration value="RECORD"/>
+            <xs:enumeration value="TEMI"/>
+            <xs:enumeration value="MMTP"/>
+            <xs:enumeration value="DOWNLOAD"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="avFilterSettings">
+        <xs:attribute name="isPassthrough" type="xs:boolean" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="sectionFilterSettings">
+        <xs:attribute name="isCheckCrc" type="xs:boolean" use="required"/>
+        <xs:attribute name="isRepeat" type="xs:boolean" use="required"/>
+        <xs:attribute name="isRaw" type="xs:boolean" use="required"/>
+    </xs:complexType>
+    <xs:complexType name="recordFilterSettings">
+        <xs:attribute name="tsIndexMask" type="tsIndexMask" use="required"/>
+        <xs:attribute name="scIndexType" type="scIndexTypeEnum" use="required"/>
+    </xs:complexType>
+
+    <xs:complexType name="filter">
+        <xs:annotation>
+            <xs:documentation>
+                Each filter element contain the following attributes:
+                    "id": unique id of the filter that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "mainType": the main filter type. The enums are defined in the xsd.
+                    "subType": the sub filter type. The enums are defined in the xsd.
+                    "bufferSize": the buffer size of the filter in hex.
+                    "pid": the pid that would be used to configure the filter.
+                    "useFMQ": if the filter uses FMQ.
+
+                Each filter element also contains at most one type-related "filterSettings".
+                    - The settings type should match the filter "subType" attribute.
+                    - For example, when filter subType is audio or video, the avFilterSettings
+                        can be configured.
+                    - This is optional and skipping the settings would pass a setting with tpid
+                        config only to the hal.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:choice minOccurs="0" maxOccurs="1">
+            <!-- TODO: b/182519645 finish all the filter settings structures. -->
+            <xs:element name="sectionFilterSettings" type="sectionFilterSettings"/>
+            <xs:element name="avFilterSettings" type="avFilterSettings"/>
+            <xs:element name="recordFilterSettings" type="recordFilterSettings"/>
+            <!--xs:element name="pes" type="pesFilterSettings"/>
+            <xs:element name="download" type="downloadFilterSettings"/-->
+        </xs:choice>
+        <xs:attribute name="id" type="filterId" use="required"/>
+        <xs:attribute name="mainType" type="filterMainTypeEnum" use="required"/>
+        <xs:attribute name="subType" type="filterSubTypeEnum" use="required"/>
+        <xs:attribute name="bufferSize" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="pid" type="xs:nonNegativeInteger" use="optional"/>
+        <xs:attribute name="useFMQ" type="xs:boolean" use="required"/>
+    </xs:complexType>
+
+    <!-- DVR SESSION -->
+    <xs:simpleType name="dvrId">
+        <!-- Dvr id must be DVR_TYPE_NUM. <dvr id="DVR_PLAYBACK_0"/> -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="DVR_RECORD_[0-9]+|DVR_PLAYBACK_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrStatusMask">
+        <!-- Dvr status mask must masking the <dvrStatusEnum> -->
+        <xs:restriction base="xs:integer">
+            <xs:minInclusive value="0"/>
+            <xs:maxInclusive value="15"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="dvrStatusEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="DATA_READY" />
+            <xs:enumeration value="LOW_WATER" />
+            <xs:enumeration value="HIGH_WATER" />
+            <xs:enumeration value="OVERFLOW" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrTypeEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="PLAYBACK" />
+            <xs:enumeration value="RECORD" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="dvrDataFormatEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="TS" />
+            <xs:enumeration value="PES" />
+            <xs:enumeration value="ES" />
+            <xs:enumeration value="SHV_TLV" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="dvr">
+        <xs:annotation>
+            <xs:documentation>
+                Each dvr element contain the following attributes:
+                    "id": unique id of the dvr that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "type": the dvr type.
+                    "bufferSize": the dvr buffer size.
+                    "statusMask": register callbacks of specific status.
+                    "lowThreshold": the dvr status low threshold.
+                    "highThreshold": the dvr status high threshold.
+                    "dataFormat": the dvr data format.
+                    "packetSize": the dvr packet size.
+                    "inputFilePath": the dvr playback input file path. Only required in playback
+                        dvr.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="id" type="dvrId" use="required"/>
+        <xs:attribute name="type" type="dvrTypeEnum" use="required"/>
+        <xs:attribute name="bufferSize" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="statusMask" type="dvrStatusMask" use="required"/>
+        <xs:attribute name="lowThreshold" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="highThreshold" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="dataFormat" type="dvrDataFormatEnum" use="required"/>
+        <xs:attribute name="packetSize" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="inputFilePath" type="xs:anyURI" use="optional"/>
+    </xs:complexType>
+
+    <!-- LNB SESSION -->
+    <xs:simpleType name="lnbId">
+        <!-- Lnb id must be LNB_NUM: <lnb id="LNB_10"/> -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="LNB_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:simpleType name="lnbVoltageEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="NONE" />
+            <xs:enumeration value="VOLTAGE_5V" />
+            <xs:enumeration value="VOLTAGE_11V" />
+            <xs:enumeration value="VOLTAGE_12V"/>
+            <xs:enumeration value="VOLTAGE_13V"/>
+            <xs:enumeration value="VOLTAGE_14V"/>
+            <xs:enumeration value="VOLTAGE_15V"/>
+            <xs:enumeration value="VOLTAGE_18V"/>
+            <xs:enumeration value="VOLTAGE_19V"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="lnbToneEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="NONE" />
+            <xs:enumeration value="CONTINUOUS" />
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="lnbPositionEnum">
+        <xs:restriction base="xs:string">
+            <xs:enumeration value="UNDEFINED" />
+            <xs:enumeration value="POSITION_A" />
+            <xs:enumeration value="POSITION_B" />
+        </xs:restriction>
+    </xs:simpleType>
+
+    <!-- Diseqc Messages that would be used to send to the lnb under test. -->
+    <xs:simpleType name="diseqcMsgName">
+        <xs:restriction base="xs:string">
+            <xs:pattern value="[A-Z_]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="diseqcMsgBody">
+        <xs:list itemType="xs:unsignedByte"/>
+    </xs:simpleType>
+    <xs:complexType name="diseqcMessage">
+        <xs:attribute name="msgName" type="diseqcMsgName"/>
+        <xs:attribute name="msgBody" type="diseqcMsgBody"/>
+    </xs:complexType>
+    <xs:simpleType name="diseqcMsgSender">
+        <xs:list itemType="diseqcMsgName"/>
+    </xs:simpleType>
+
+    <xs:complexType name="lnb">
+        <xs:annotation>
+            <xs:documentation>
+                Each lnb element contain the following attributes:
+                    "id": unique id of the lnb that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "name": the external lnb device name.
+                    "voltage": the voltage used to config the lnb.
+                    "tone": the voltage used to config the lnb.
+                    "position": the voltage used to config the lnb.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="id" type="lnbId" use="required"/>
+        <!-- Only required on external lnb with no device id. -->
+        <xs:attribute name="name" type="xs:string" use="optional"/>
+        <xs:attribute name="voltage" type="lnbVoltageEnum" use="required"/>
+        <xs:attribute name="tone" type="lnbToneEnum" use="required"/>
+        <xs:attribute name="position" type="lnbPositionEnum" use="required"/>
+    </xs:complexType>
+
+    <!-- TIME FILTER SESSION -->
+    <xs:simpleType name="timeFilterId">
+        <!-- Time Filter id must be TIME_FILTER_NUM: <timeFilter id="TIME_FILTER_1"/> -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="TIME_FILTER_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+
+    <xs:complexType name="timeFilter">
+        <xs:annotation>
+            <xs:documentation>
+                Each timeFilter element contain the following attributes:
+                    "id": unique id of the time filter that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "timeStamp": the time stamp used to config the time filter.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="id" type="timeFilterId" use="required"/>
+        <xs:attribute name="timeStamp" type="xs:nonNegativeInteger" use="required"/>
+    </xs:complexType>
+
+    <!-- DESCRAMBLER SESSION -->
+    <xs:simpleType name="descramblerId">
+        <!-- Descrambler id must be DESCRAMBLER_NUM: <descrambler id="DESCRAMBLER_2"/> -->
+        <xs:restriction base="xs:string">
+            <xs:pattern value="DESCRAMBLER_[0-9]+"/>
+        </xs:restriction>
+    </xs:simpleType>
+    <xs:simpleType name="sessionPrivateData">
+        <xs:list itemType="xs:unsignedByte"/>
+    </xs:simpleType>
+
+    <xs:complexType name="descrambler">
+        <xs:annotation>
+            <xs:documentation>
+                Each descrambler element contain the following attributes:
+                    "id": unique id of the descrambler that could be used to connect to the test the
+                        "dataFlowConfiguration"
+                    "casSystemId": the cas system id to connect to the descrambler.
+                    "provisionStr": the provision string to use with the cas plugin.
+                    "sesstionPrivatData": the session private data used to open the cas session.
+            </xs:documentation>
+        </xs:annotation>
+        <xs:attribute name="id" type="descramblerId" use="required"/>
+        <xs:attribute name="casSystemId" type="xs:nonNegativeInteger" use="required"/>
+        <xs:attribute name="provisionStr" type="xs:string" use="required"/>
+        <xs:attribute name="sesstionPrivatData" type="sessionPrivateData" use="optional"/>
+    </xs:complexType>
+
+    <!-- HARDWARE CONFIGURATION SESSION -->
+    <xs:complexType name="hardwareConfiguration">
+        <xs:sequence>
+            <xs:element name="frontends" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the frontends that would be
+                                used in the tests.
+                                - This section is optional and can be skipped to use the default
+                                    fe settings.
+                                - The default settings can be found in the
+                                    sample_tuner_vts_configurations.xml.
+                                - The users can also override the default frontend settings using
+                                    id="FE_DEFAULT".
+                                - The users can configure 1 or more frontend elements in the
+                                    frontends sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="frontend" type="frontend" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="filters" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the filters that would be
+                                used in the tests.
+                                - This section is optional and can be skipped to use the default
+                                    filter settings.
+                                - The default settings can be found in the
+                                    sample_tuner_vts_configurations.xml.
+                                - The users can also override the default filter settings using
+                                - id="FILTER_AUDIO_DEFAULT" or "FILTER_VIDEO_DEFAULT".
+                                - The users can configure 1 or more filter elements in the filters
+                                    sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="filter" type="filter" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrs" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the dvrs that would be used
+                                in the tests.
+                                - This section is optional and can be skipped if the device does
+                                    not support dvr.
+                                - The users can configure 1 or more dvr elements in the dvrs
+                                   sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="dvr" type="dvr" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="diseqcMessages" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the diseqc messages that
+                            would be used in the lnb tests.
+                                - This section is optional and can be skipped if lnb is not suppoted
+                                - The users can configure 1 or more message elements in the
+                                    diseqcMessages sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="diseqcMessage" type="diseqcMessage" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="lnbs" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the lnbs that would be used
+                                in the tests.
+                                - This section is optional and can be skipped if lnb is not suppoted
+                                - The users can configure 1 or more lnb elements in the lnbs
+                                    sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="lnb" type="lnb" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="timeFilters" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the time filters that would
+                                be used in the tests.
+                                - This section is optional and can be skipped if time filter is
+                                    not supported.
+                                - The users can configure 1 or more time filter elements in the
+                                    time filters sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="timeFilter" type="timeFilter" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="descramblers" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:annotation>
+                        <xs:documentation xml:lang="en">
+                            This section contains configurations of all the descramblers that would
+                                be used in the tests.
+                                - This section is optional and can be skipped if descrambling is not
+                                    supported.
+                                - The users can configure 1 or more descrambler elements in the
+                                    descramblers sections.
+                        </xs:documentation>
+                    </xs:annotation>
+                    <xs:sequence>
+                        <xs:element name="descrambler" type="descrambler" minOccurs="1" maxOccurs="unbounded"/>
+                    </xs:sequence>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- DATA FLOW CONFIGURATION SESSION -->
+    <xs:complexType name="dataFlowConfiguration">
+        <xs:sequence>
+            <!-- clearLiveBroadcast is only optional when there is no physical frontend. In this
+              case, the dvrPlayback config is required. -->
+            <xs:element name="clearLiveBroadcast" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="pcrFilterConnection" type="filterId" use="optional"/>
+                    <xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
+                    <!-- TODO: b/182519645 allow the users to insert extra filters -->
+                    <!-- DVR is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <!-- scan is only optional when there is no physical frontend. In this case, the
+              dvrPlayback config is required. -->
+            <xs:element name="scan" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="descrambling" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <!-- If there is a software or hardware frontend connection or not. If false,
+                      dvrSourceConnection config is required when testing dvrRecord.  -->
+                    <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="descramblerConnection" type="descramblerId" use="required"/>
+                    <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+                    <!-- TODO: b/182519645 allow the users to insert extra filters -->
+                    <!-- This DVR is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                    <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+                    <xs:attribute name="dvrSourceConnection" type="dvrId" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrPlayback" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="dvrConnection" type="dvrId" use="required"/>
+                    <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="sectionFilterConnection" type="filterId" use="optional"/>
+                    <!-- TODO: b/182519645 allow the users to insert extra filters -->
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="dvrRecord" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <!-- If there is a software or hardware frontend connection or not. If false,
+                      dvrSourceConnection config is required when testing dvrRecord.  -->
+                    <xs:attribute name="hasFrontendConnection" type="xs:boolean" use="required"/>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="optional"/>
+                    <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+                    <!-- This Dvr is only required when the frontend is using the software input -->
+                    <xs:attribute name="dvrSoftwareFeConnection" type="dvrId" use="optional"/>
+                    <!-- This Dvr is only required when there's no frontend(sw or hw) connection -->
+                    <xs:attribute name="dvrSourceConnection" type="dvrId" use="optional"/>
+                    <xs:attribute name="recordFilterConnection" type="filterId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="lnbLive" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="audioFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="videoFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+                    <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+                    <!-- TODO: b/182519645 allow the users to insert extra filters -->
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="lnbRecord" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="frontendConnection" type="frontendId" use="required"/>
+                    <xs:attribute name="recordFilterConnection" type="filterId" use="required"/>
+                    <xs:attribute name="dvrRecordConnection" type="dvrId" use="required"/>
+                    <xs:attribute name="lnbConnection" type="lnbId" use="required"/>
+                    <xs:attribute name="diseqcMsgSender" type="diseqcMsgSender" use="optional"/>
+                </xs:complexType>
+            </xs:element>
+            <xs:element name="timeFilter" minOccurs="0" maxOccurs="1">
+                <xs:complexType>
+                    <xs:attribute name="timeFilterConnection" type="timeFilterId" use="required"/>
+                </xs:complexType>
+            </xs:element>
+        </xs:sequence>
+    </xs:complexType>
+
+    <!-- Full Tuner Configuration. This is the root element of the configuration xml. -->
+    <xs:element name="TunerConfiguration">
+        <xs:complexType>
+            <xs:sequence>
+                <xs:element name="hardwareConfiguration" type="hardwareConfiguration" minOccurs="1" maxOccurs="1"/>
+                <xs:element name="dataFlowConfiguration" type="dataFlowConfiguration" minOccurs="1" maxOccurs="1"/>
+            </xs:sequence>
+            <xs:attribute name="version" type="version"/>
+        </xs:complexType>
+        <xs:key name="frontendIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/frontends/frontend"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+        <xs:key name="filterIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/filters/filter"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+        <xs:key name="dvrIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/dvrs/dvr"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+        <xs:key name="lnbIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/lnbs/lnb"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+        <xs:key name="timeFilterIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/timeFilters/timeFilter"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+        <xs:key name="descramblerIdUniqueness">
+            <xs:selector xpath="hardwareConfiguration/descramblers/descrambler"/>
+            <xs:field xpath="@id"/>
+        </xs:key>
+    </xs:element>
+</xs:schema>
diff --git a/update-base-files.sh b/update-base-files.sh
index daaa530..d01847d 100755
--- a/update-base-files.sh
+++ b/update-base-files.sh
@@ -42,8 +42,11 @@
 
 # system/media
 hidl-gen $options \
+         -o $ANDROID_BUILD_TOP/system/media/audio/include/system/audio_common-base.h \
+         android.hardware.audio.common@7.0
+hidl-gen $options \
          -o $ANDROID_BUILD_TOP/system/media/audio/include/system/audio-base.h \
-         android.hardware.audio.common@2.0
+         android.hardware.audio@7.0
 hidl-gen $options \
          -o $ANDROID_BUILD_TOP/system/media/audio/include/system/audio_effect-base.h \
-         android.hardware.audio.effect@2.0
+         android.hardware.audio.effect@7.0
diff --git a/usb/1.0/Android.bp b/usb/1.0/Android.bp
index c0e883f..d5d3500 100644
--- a/usb/1.0/Android.bp
+++ b/usb/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.usb@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IUsb.hal",
diff --git a/usb/1.0/default/Android.bp b/usb/1.0/default/Android.bp
index 98d9064..5f56fe0 100644
--- a/usb/1.0/default/Android.bp
+++ b/usb/1.0/default/Android.bp
@@ -12,6 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.usb@1.0-service",
     defaults: ["hidl_defaults"],
diff --git a/usb/1.0/vts/functional/Android.bp b/usb/1.0/vts/functional/Android.bp
index ae31bd2..d976a06 100644
--- a/usb/1.0/vts/functional/Android.bp
+++ b/usb/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalUsbV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/usb/1.0/vts/functional/VtsHalUsbV1_0TargetTest.cpp b/usb/1.0/vts/functional/VtsHalUsbV1_0TargetTest.cpp
index bba75c8..99ca249 100644
--- a/usb/1.0/vts/functional/VtsHalUsbV1_0TargetTest.cpp
+++ b/usb/1.0/vts/functional/VtsHalUsbV1_0TargetTest.cpp
@@ -298,6 +298,7 @@
   }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, UsbHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IUsb::descriptor)),
diff --git a/usb/1.1/Android.bp b/usb/1.1/Android.bp
index 6f2abae..9c3e0dc 100644
--- a/usb/1.1/Android.bp
+++ b/usb/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.usb@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IUsb.hal",
diff --git a/usb/1.1/vts/functional/Android.bp b/usb/1.1/vts/functional/Android.bp
index 5bec94a..f514009 100644
--- a/usb/1.1/vts/functional/Android.bp
+++ b/usb/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalUsbV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -24,4 +33,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp b/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
index d4b2ffd..19830a6 100644
--- a/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
+++ b/usb/1.1/vts/functional/VtsHalUsbV1_1TargetTest.cpp
@@ -169,6 +169,7 @@
     EXPECT_EQ(PortMode::NONE, res.args->usb_last_port_status.status.supportedModes);
     EXPECT_EQ(Status::SUCCESS, res.args->usb_last_status);
 }
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(UsbHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, UsbHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IUsb::descriptor)),
diff --git a/usb/1.2/Android.bp b/usb/1.2/Android.bp
index b3ba81f..0d4430d 100644
--- a/usb/1.2/Android.bp
+++ b/usb/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.usb@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IUsb.hal",
diff --git a/usb/1.2/vts/functional/Android.bp b/usb/1.2/vts/functional/Android.bp
index d6aaf2d..688e725 100644
--- a/usb/1.2/vts/functional/Android.bp
+++ b/usb/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalUsbV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/usb/gadget/1.0/Android.bp b/usb/gadget/1.0/Android.bp
index 4921abf..bb602fa 100644
--- a/usb/gadget/1.0/Android.bp
+++ b/usb/gadget/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.usb.gadget@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IUsbGadget.hal",
diff --git a/usb/gadget/1.1/Android.bp b/usb/gadget/1.1/Android.bp
index b41eb9c..388f11c 100644
--- a/usb/gadget/1.1/Android.bp
+++ b/usb/gadget/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.usb.gadget@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IUsbGadget.hal",
     ],
diff --git a/usb/gadget/1.1/default/Android.bp b/usb/gadget/1.1/default/Android.bp
index 68e2a29..789a4f0 100644
--- a/usb/gadget/1.1/default/Android.bp
+++ b/usb/gadget/1.1/default/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.usb.gadget@1.1-service",
     defaults: ["hidl_defaults"],
diff --git a/usb/gadget/1.1/default/lib/Android.bp b/usb/gadget/1.1/default/lib/Android.bp
index bba8340..bf7bb37 100644
--- a/usb/gadget/1.1/default/lib/Android.bp
+++ b/usb/gadget/1.1/default/lib/Android.bp
@@ -14,6 +14,15 @@
  * limitations under the License.
  */
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libusbconfigfs",
     vendor_available: true,
diff --git a/vibrator/1.0/Android.bp b/vibrator/1.0/Android.bp
index 792e130..1ae1cef 100644
--- a/vibrator/1.0/Android.bp
+++ b/vibrator/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.vibrator@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IVibrator.hal",
diff --git a/vibrator/1.0/default/Android.bp b/vibrator/1.0/default/Android.bp
index b0d0986..ed750b4 100644
--- a/vibrator/1.0/default/Android.bp
+++ b/vibrator/1.0/default/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.vibrator@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/vibrator/1.0/vts/functional/Android.bp b/vibrator/1.0/vts/functional/Android.bp
index 4ec1aa8..83377e7 100644
--- a/vibrator/1.0/vts/functional/Android.bp
+++ b/vibrator/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVibratorV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -21,4 +30,3 @@
     static_libs: ["android.hardware.vibrator@1.0"],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp b/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp
index 2aee338..863bfae 100644
--- a/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp
+++ b/vibrator/1.0/vts/functional/VtsHalVibratorV1_0TargetTest.cpp
@@ -134,6 +134,7 @@
   }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, VibratorHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVibrator::descriptor)),
diff --git a/vibrator/1.1/Android.bp b/vibrator/1.1/Android.bp
index 0d04a87..357c1f0 100644
--- a/vibrator/1.1/Android.bp
+++ b/vibrator/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.vibrator@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IVibrator.hal",
diff --git a/vibrator/1.1/vts/functional/Android.bp b/vibrator/1.1/vts/functional/Android.bp
index b291e7c..f97a343 100644
--- a/vibrator/1.1/vts/functional/Android.bp
+++ b/vibrator/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVibratorV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -24,4 +33,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/vibrator/1.1/vts/functional/VtsHalVibratorV1_1TargetTest.cpp b/vibrator/1.1/vts/functional/VtsHalVibratorV1_1TargetTest.cpp
index da94308..e7ec496 100644
--- a/vibrator/1.1/vts/functional/VtsHalVibratorV1_1TargetTest.cpp
+++ b/vibrator/1.1/vts/functional/VtsHalVibratorV1_1TargetTest.cpp
@@ -108,6 +108,7 @@
     EXPECT_OK(vibrator->perform_1_1(Effect_1_1::CLICK, badStrength, validatePerformEffectBadInput));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorHidlTest_1_1);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, VibratorHidlTest_1_1,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVibrator::descriptor)),
diff --git a/vibrator/1.2/Android.bp b/vibrator/1.2/Android.bp
index 290a0cf..42f97d4 100644
--- a/vibrator/1.2/Android.bp
+++ b/vibrator/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.vibrator@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IVibrator.hal",
diff --git a/vibrator/1.2/vts/functional/Android.bp b/vibrator/1.2/vts/functional/Android.bp
index 7bf69d0..40171ae 100644
--- a/vibrator/1.2/vts/functional/Android.bp
+++ b/vibrator/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVibratorV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -25,4 +34,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp b/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp
index 2058e85..b96e269 100644
--- a/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp
+++ b/vibrator/1.2/vts/functional/VtsHalVibratorV1_2TargetTest.cpp
@@ -116,6 +116,7 @@
     EXPECT_OK(vibrator->perform_1_2(Effect::THUD, badStrength, validatePerformEffectBadInput));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorHidlTest_1_2);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, VibratorHidlTest_1_2,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVibrator::descriptor)),
diff --git a/vibrator/1.3/Android.bp b/vibrator/1.3/Android.bp
index 357ea9a..124f5d1 100644
--- a/vibrator/1.3/Android.bp
+++ b/vibrator/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.vibrator@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IVibrator.hal",
diff --git a/vibrator/1.3/example/Android.bp b/vibrator/1.3/example/Android.bp
index 07f1c26..7e9bf8f 100644
--- a/vibrator/1.3/example/Android.bp
+++ b/vibrator/1.3/example/Android.bp
@@ -13,6 +13,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_binary {
     name: "android.hardware.vibrator@1.3-service.example",
     vendor: true,
diff --git a/vibrator/1.3/vts/functional/Android.bp b/vibrator/1.3/vts/functional/Android.bp
index 5215ed0..0fcbf07 100644
--- a/vibrator/1.3/vts/functional/Android.bp
+++ b/vibrator/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVibratorV1_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -26,4 +35,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp b/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
index 3cd3430..84a6af0 100644
--- a/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
+++ b/vibrator/1.3/vts/functional/VtsHalVibratorV1_3TargetTest.cpp
@@ -131,6 +131,7 @@
                                     validatePerformEffectUnsupportedOperation));
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorHidlTest_1_3);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, VibratorHidlTest_1_3,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVibrator::descriptor)),
diff --git a/vibrator/aidl/Android.bp b/vibrator/aidl/Android.bp
index 9766353..9bad971 100644
--- a/vibrator/aidl/Android.bp
+++ b/vibrator/aidl/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 aidl_interface {
     name: "android.hardware.vibrator",
     vendor_available: true,
diff --git a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
index 8cb259f..7431804 100644
--- a/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/aidl_api/android.hardware.vibrator/current/android/hardware/vibrator/CompositeEffect.aidl
@@ -19,6 +19,6 @@
 @VintfStability
 parcelable CompositeEffect {
   int delayMs;
-  android.hardware.vibrator.CompositePrimitive primitive;
+  android.hardware.vibrator.CompositePrimitive primitive = android.hardware.vibrator.CompositePrimitive.NOOP;
   float scale;
 }
diff --git a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
index 406a899..5a990c0 100644
--- a/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/CompositeEffect.aidl
@@ -22,7 +22,7 @@
 parcelable CompositeEffect {
     /* Period of silence preceding primitive. */
     int delayMs;
-    CompositePrimitive primitive;
+    CompositePrimitive primitive = CompositePrimitive.NOOP;
     /*
      * 0.0 (inclusive) - 1.0 (inclusive),
      * where 0.0 is minimum "feelable" amplitude.
diff --git a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
index 0b21248..cd7b603 100644
--- a/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
+++ b/vibrator/aidl/android/hardware/vibrator/IVibrator.aidl
@@ -206,7 +206,10 @@
      * device/board configuration files ensuring that no ID is assigned to
      * multiple clients. No client should use this API unless explicitly
      * assigned an always-on source ID. Clients must develop their own way to
-     * get IDs from vendor in a stable way.
+     * get IDs from vendor in a stable way. For instance, a client may expose
+     * a stable API (via HAL, sysprops, or xml overlays) to allow vendor to
+     * associate a hardware ID with a specific usecase. When that usecase is
+     * triggered, a client would use that hardware ID here.
      *
      * @param id The device-specific always-on source ID to enable.
      * @param effect The type of haptic event to trigger.
diff --git a/vibrator/aidl/default/Android.bp b/vibrator/aidl/default/Android.bp
index 9e6d9cf..0f9850f 100644
--- a/vibrator/aidl/default/Android.bp
+++ b/vibrator/aidl/default/Android.bp
@@ -1,10 +1,19 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "libvibratorexampleimpl",
     vendor: true,
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-V1-ndk_platform",
     ],
     export_include_dirs: ["include"],
     srcs: ["Vibrator.cpp"],
@@ -23,7 +32,7 @@
     shared_libs: [
         "libbase",
         "libbinder_ndk",
-        "android.hardware.vibrator-ndk_platform",
+        "android.hardware.vibrator-V1-ndk_platform",
     ],
     static_libs: [
         "libvibratorexampleimpl",
diff --git a/vibrator/aidl/default/Vibrator.cpp b/vibrator/aidl/default/Vibrator.cpp
index b359100..322833b 100644
--- a/vibrator/aidl/default/Vibrator.cpp
+++ b/vibrator/aidl/default/Vibrator.cpp
@@ -125,6 +125,11 @@
 
 ndk::ScopedAStatus Vibrator::getPrimitiveDuration(CompositePrimitive primitive,
                                                   int32_t* durationMs) {
+    std::vector<CompositePrimitive> supported;
+    getSupportedPrimitives(&supported);
+    if (std::find(supported.begin(), supported.end(), primitive) == supported.end()) {
+        return ndk::ScopedAStatus::fromExceptionCode(EX_UNSUPPORTED_OPERATION);
+    }
     if (primitive != CompositePrimitive::NOOP) {
         *durationMs = 100;
     } else {
@@ -163,6 +168,10 @@
             }
             LOG(INFO) << "triggering primitive " << static_cast<int>(e.primitive) << " @ scale "
                       << e.scale;
+
+            int32_t durationMs;
+            getPrimitiveDuration(e.primitive, &durationMs);
+            usleep(durationMs * 1000);
         }
 
         if (callback != nullptr) {
diff --git a/vibrator/aidl/vts/Android.bp b/vibrator/aidl/vts/Android.bp
index 28cb4d9..1b6e56d 100644
--- a/vibrator/aidl/vts/Android.bp
+++ b/vibrator/aidl/vts/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVibratorTargetTest",
     defaults: [
@@ -9,7 +18,7 @@
         "libbinder",
     ],
     static_libs: [
-        "android.hardware.vibrator-cpp",
+        "android.hardware.vibrator-V1-cpp",
     ],
     test_suites: [
         "general-tests",
diff --git a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
index 8340517..e51f594 100644
--- a/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
+++ b/vibrator/aidl/vts/VtsHalVibratorTargetTest.cpp
@@ -34,6 +34,7 @@
 using android::hardware::vibrator::Effect;
 using android::hardware::vibrator::EffectStrength;
 using android::hardware::vibrator::IVibrator;
+using std::chrono::high_resolution_clock;
 
 const std::vector<Effect> kEffects{android::enum_range<Effect>().begin(),
                                    android::enum_range<Effect>().end()};
@@ -95,7 +96,7 @@
 }
 
 TEST_P(VibratorAidl, OnWithCallback) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) return;
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) return;
 
     std::promise<void> completionPromise;
     std::future<void> completionFuture{completionPromise.get_future()};
@@ -109,7 +110,7 @@
 }
 
 TEST_P(VibratorAidl, OnCallbackNotSupported) {
-    if (!(capabilities & IVibrator::CAP_PERFORM_CALLBACK)) {
+    if (!(capabilities & IVibrator::CAP_ON_CALLBACK)) {
         sp<CompletionCallback> callback = new CompletionCallback([] {});
         EXPECT_EQ(Status::EX_UNSUPPORTED_OPERATION, vibrator->on(250, callback).exceptionCode());
     }
@@ -442,26 +443,52 @@
 }
 
 TEST_P(VibratorAidl, ComposeCallback) {
+    constexpr std::chrono::milliseconds allowedLatency{10};
+
     if (capabilities & IVibrator::CAP_COMPOSE_EFFECTS) {
-        std::promise<void> completionPromise;
-        std::future<void> completionFuture{completionPromise.get_future()};
-        sp<CompletionCallback> callback =
-                new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
-        CompositePrimitive primitive = CompositePrimitive::CLICK;
-        CompositeEffect effect;
-        std::vector<CompositeEffect> composite;
-        int32_t duration;
+        std::vector<CompositePrimitive> supported;
 
-        effect.delayMs = 0;
-        effect.primitive = primitive;
-        effect.scale = 1.0f;
-        composite.emplace_back(effect);
+        ASSERT_TRUE(vibrator->getSupportedPrimitives(&supported).isOk());
 
-        EXPECT_EQ(Status::EX_NONE,
-                  vibrator->getPrimitiveDuration(primitive, &duration).exceptionCode());
-        EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode());
-        EXPECT_EQ(completionFuture.wait_for(std::chrono::milliseconds(duration * 2)),
-                  std::future_status::ready);
+        for (auto primitive : supported) {
+            if (primitive == CompositePrimitive::NOOP) {
+                continue;
+            }
+
+            std::promise<void> completionPromise;
+            std::future<void> completionFuture{completionPromise.get_future()};
+            sp<CompletionCallback> callback =
+                    new CompletionCallback([&completionPromise] { completionPromise.set_value(); });
+            CompositeEffect effect;
+            std::vector<CompositeEffect> composite;
+            int32_t durationMs;
+            std::chrono::milliseconds duration;
+            std::chrono::time_point<high_resolution_clock> start, end;
+            std::chrono::milliseconds elapsed;
+
+            effect.delayMs = 0;
+            effect.primitive = primitive;
+            effect.scale = 1.0f;
+            composite.emplace_back(effect);
+
+            EXPECT_EQ(Status::EX_NONE,
+                      vibrator->getPrimitiveDuration(primitive, &durationMs).exceptionCode())
+                    << toString(primitive);
+            duration = std::chrono::milliseconds(durationMs);
+
+            EXPECT_EQ(Status::EX_NONE, vibrator->compose(composite, callback).exceptionCode())
+                    << toString(primitive);
+            start = high_resolution_clock::now();
+
+            EXPECT_EQ(completionFuture.wait_for(duration + allowedLatency),
+                      std::future_status::ready)
+                    << toString(primitive);
+            end = high_resolution_clock::now();
+
+            elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(end - start);
+            EXPECT_LE(elapsed.count(), (duration + allowedLatency).count()) << toString(primitive);
+            EXPECT_GE(elapsed.count(), (duration - allowedLatency).count()) << toString(primitive);
+        }
     }
 }
 
@@ -491,6 +518,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VibratorAidl);
 INSTANTIATE_TEST_SUITE_P(Vibrator, VibratorAidl,
                          testing::ValuesIn(android::getAidlHalInstanceNames(IVibrator::descriptor)),
                          android::PrintInstanceNameToString);
diff --git a/vr/1.0/Android.bp b/vr/1.0/Android.bp
index f91f874..27d2ea8 100644
--- a/vr/1.0/Android.bp
+++ b/vr/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.vr@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IVr.hal",
     ],
diff --git a/vr/1.0/default/Android.bp b/vr/1.0/default/Android.bp
index cfb2808..6e59bcf 100644
--- a/vr/1.0/default/Android.bp
+++ b/vr/1.0/default/Android.bp
@@ -1,3 +1,12 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_shared {
     name: "android.hardware.vr@1.0-impl",
     defaults: ["hidl_defaults"],
diff --git a/vr/1.0/vts/functional/Android.bp b/vr/1.0/vts/functional/Android.bp
index 6bfa05c..8e23f4f 100644
--- a/vr/1.0/vts/functional/Android.bp
+++ b/vr/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalVrV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp b/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
index c08e5ca..049ec73 100644
--- a/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
+++ b/vr/1.0/vts/functional/VtsHalVrV1_0TargetTest.cpp
@@ -63,6 +63,7 @@
   EXPECT_TRUE(vr->setVrMode(false).isOk());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(VrHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, VrHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVr::descriptor)),
diff --git a/weaver/1.0/Android.bp b/weaver/1.0/Android.bp
index 7d5b8fe..f7d452a 100644
--- a/weaver/1.0/Android.bp
+++ b/weaver/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.weaver@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IWeaver.hal",
diff --git a/weaver/1.0/vts/functional/Android.bp b/weaver/1.0/vts/functional/Android.bp
index b20f127..cc1d284 100644
--- a/weaver/1.0/vts/functional/Android.bp
+++ b/weaver/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWeaverV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
index ae92c36..66465a9 100644
--- a/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
+++ b/weaver/1.0/vts/functional/VtsHalWeaverV1_0TargetTest.cpp
@@ -336,6 +336,7 @@
     EXPECT_EQ(timeout, 0u);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverHidlTest);
 INSTANTIATE_TEST_SUITE_P(
         PerInstance, WeaverHidlTest,
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(IWeaver::descriptor)),
diff --git a/weaver/aidl/Android.bp b/weaver/aidl/Android.bp
new file mode 100644
index 0000000..60925ec
--- /dev/null
+++ b/weaver/aidl/Android.bp
@@ -0,0 +1,25 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+aidl_interface {
+    name: "android.hardware.weaver",
+    vendor_available: true,
+    srcs: ["android/hardware/weaver/*.aidl"],
+    stability: "vintf",
+    backend: {
+        java: {
+            platform_apis: true,
+        },
+        ndk: {
+            vndk: {
+                enabled: true,
+            },
+        },
+    },
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
new file mode 100644
index 0000000..61627d9
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/IWeaver.aidl
@@ -0,0 +1,43 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+interface IWeaver {
+  android.hardware.weaver.WeaverConfig getConfig();
+  android.hardware.weaver.WeaverReadResponse read(in int slotId, in byte[] key);
+  void write(in int slotId, in byte[] key, in byte[] value);
+  const int STATUS_FAILED = 1;
+  const int STATUS_INCORRECT_KEY = 2;
+  const int STATUS_THROTTLE = 3;
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
new file mode 100644
index 0000000..7491f32
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverConfig.aidl
@@ -0,0 +1,40 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+parcelable WeaverConfig {
+  int slots;
+  int keySize;
+  int valueSize;
+}
diff --git a/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
new file mode 100644
index 0000000..47ee4c8
--- /dev/null
+++ b/weaver/aidl/aidl_api/android.hardware.weaver/current/android/hardware/weaver/WeaverReadResponse.aidl
@@ -0,0 +1,39 @@
+/*
+ * Copyright 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.
+ */
+///////////////////////////////////////////////////////////////////////////////
+// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE.                          //
+///////////////////////////////////////////////////////////////////////////////
+
+// This file is a snapshot of an AIDL file. Do not edit it manually. There are
+// two cases:
+// 1). this is a frozen version file - do not edit this in any case.
+// 2). this is a 'current' file. If you make a backwards compatible change to
+//     the interface (from the latest frozen version), the build system will
+//     prompt you to update this file with `m <name>-update-api`.
+//
+// You must not make a backward incompatible change to any AIDL file built
+// with the aidl_interface module type with versions property set. The module
+// type is used to build AIDL files in a way that they can be used across
+// independently updatable components of the system. If a device is shipped
+// with such a backward incompatible change, it has a high risk of breaking
+// later when a module using the interface is updated, e.g., Mainline modules.
+
+package android.hardware.weaver;
+@VintfStability
+parcelable WeaverReadResponse {
+  long timeout;
+  byte[] value;
+}
diff --git a/weaver/aidl/android/hardware/weaver/IWeaver.aidl b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
new file mode 100644
index 0000000..f51034a
--- /dev/null
+++ b/weaver/aidl/android/hardware/weaver/IWeaver.aidl
@@ -0,0 +1,94 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.weaver;
+
+import android.hardware.weaver.WeaverConfig;
+import android.hardware.weaver.WeaverReadResponse;
+
+/**
+ * Weaver provides secure storage of secret values that may only be read if the
+ * corresponding key has been presented.
+ *
+ * The storage must be secure as the device's user authentication and encryption
+ * relies on the security of these values. The cardinality of the domains of the
+ * key and value must be suitably large such that they cannot be easily guessed.
+ *
+ * Weaver is structured as an array of slots, each containing a key-value pair.
+ * Slots are uniquely identified by an ID in the range [0, `getConfig().slots`).
+ */
+@VintfStability
+interface IWeaver {
+    /**
+     * Retrieves the config information for this implementation of Weaver.
+     *
+     * The config is static i.e. every invocation returns the same information.
+     *
+     * @return config data for this implementation of Weaver if status is OK,
+     *         otherwise undefined.
+     */
+    WeaverConfig getConfig();
+
+    /**
+     * Read binder calls may return a ServiceSpecificException with the following error codes.
+     */
+    const int STATUS_FAILED = 1;
+    const int STATUS_INCORRECT_KEY = 2;
+    const int STATUS_THROTTLE = 3;
+
+    /**
+     * Attempts to retrieve the value stored in the identified slot.
+     *
+     * The value is only returned if the provided key matches the key stored in
+     * the slot. The value is never returned if the wrong key is provided.
+     *
+     * Throttling must be used to limit the frequency of failed read attempts.
+     * The value is only returned when throttling is not active, even if the
+     * correct key is provided. If called when throttling is active, the time
+     * until the next attempt can be made is returned.
+     *
+     * Service status return:
+     *
+     * OK if the value was successfully read from slot.
+     * INCORRECT_KEY if the key does not match the key in the slot.
+     * THROTTLE if throttling is active.
+     * STATUS_FAILED if the read was unsuccessful for another reason.
+     *
+     * @param slotId of the slot to read from, this must be positive to be valid.
+     * @param key that is stored in the slot.
+     * @return The WeaverReadResponse for this read request. If the status is OK,
+     * value is set to the value in the slot and timeout is 0. Otherwise, value is
+     * empty and timeout is set accordingly.
+     */
+    WeaverReadResponse read(in int slotId, in byte[] key);
+
+    /**
+     * Overwrites the identified slot with the provided key and value.
+     *
+     * The new values are written regardless of the current state of the slot in
+     * order to remain idempotent.
+     *
+     * Service status return:
+     *
+     * OK if the write was successfully completed.
+     * FAILED if the write was unsuccessful.
+     *
+     * @param slotId of the slot to write to.
+     * @param key to write to the slot.
+     * @param value to write to slot.
+     */
+    void write(in int slotId, in byte[] key, in byte[] value);
+}
diff --git a/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
new file mode 100644
index 0000000..a156a7b
--- /dev/null
+++ b/weaver/aidl/android/hardware/weaver/WeaverConfig.aidl
@@ -0,0 +1,34 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.weaver;
+
+@VintfStability
+parcelable WeaverConfig {
+    /**
+     * The number of slots available.
+     */
+    int slots;
+    /**
+     * The number of bytes used for a key.
+     */
+    int keySize;
+    /**
+     * The number of bytes used for a value.
+     */
+    int valueSize;
+}
+
diff --git a/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
new file mode 100644
index 0000000..ec006e8
--- /dev/null
+++ b/weaver/aidl/android/hardware/weaver/WeaverReadResponse.aidl
@@ -0,0 +1,30 @@
+/*
+ * Copyright 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.
+ */
+
+package android.hardware.weaver;
+
+@VintfStability
+parcelable WeaverReadResponse {
+    /**
+     * The time to wait, in milliseconds, before making the next request.
+     */
+    long timeout;
+    /**
+     * The value read from the slot or empty if the value was not read.
+     */
+    byte[] value;
+}
+
diff --git a/weaver/aidl/default/Android.bp b/weaver/aidl/default/Android.bp
new file mode 100644
index 0000000..37a9c94
--- /dev/null
+++ b/weaver/aidl/default/Android.bp
@@ -0,0 +1,41 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.weaver-service.example",
+    relative_install_path: "hw",
+    init_rc: ["android.hardware.weaver-service.example.rc"],
+    vintf_fragments: ["android.hardware.weaver-service.example.xml"],
+    vendor: true,
+    srcs: [
+        "service.cpp",
+        "Weaver.cpp",
+    ],
+    shared_libs: [
+        "android.hardware.weaver-V1-ndk_platform",
+        "libbase",
+        "libbinder_ndk",
+    ],
+}
diff --git a/weaver/aidl/default/Weaver.cpp b/weaver/aidl/default/Weaver.cpp
new file mode 100644
index 0000000..56d9c4d
--- /dev/null
+++ b/weaver/aidl/default/Weaver.cpp
@@ -0,0 +1,48 @@
+/*
+ * 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 "Weaver.h"
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace weaver {
+
+// Methods from ::android::hardware::weaver::IWeaver follow.
+
+::ndk::ScopedAStatus Weaver::getConfig(WeaverConfig* out_config) {
+    (void)out_config;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Weaver::read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* out_response) {
+    (void)in_slotId;
+    (void)in_key;
+    (void)out_response;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus Weaver::write(int32_t in_slotId, const std::vector<uint8_t>& in_key, const std::vector<uint8_t>& in_value) {
+    (void)in_slotId;
+    (void)in_key;
+    (void)in_value;
+    return ::ndk::ScopedAStatus::ok();
+}
+
+} //namespace weaver
+} //namespace hardware
+} //namespace android
+} //namespace aidl
diff --git a/weaver/aidl/default/Weaver.h b/weaver/aidl/default/Weaver.h
new file mode 100644
index 0000000..b50018e
--- /dev/null
+++ b/weaver/aidl/default/Weaver.h
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <aidl/android/hardware/weaver/BnWeaver.h>
+
+namespace aidl {
+namespace android {
+namespace hardware {
+namespace weaver {
+
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+
+struct Weaver : public BnWeaver {
+public:
+    Weaver() = default;
+
+    // Methods from ::android::hardware::weaver::IWeaver follow.
+    ::ndk::ScopedAStatus getConfig(WeaverConfig* _aidl_return) override;
+    ::ndk::ScopedAStatus read(int32_t in_slotId, const std::vector<uint8_t>& in_key, WeaverReadResponse* _aidl_return) override;
+    ::ndk::ScopedAStatus write(int32_t in_slotId, const std::vector<uint8_t>& in_key, const std::vector<uint8_t>& in_value) override;
+};
+
+}  // namespace weaver
+}  // namespace hardware
+}  // namespace android
+}  // namespace aidl
diff --git a/weaver/aidl/default/android.hardware.weaver-service.example.rc b/weaver/aidl/default/android.hardware.weaver-service.example.rc
new file mode 100644
index 0000000..ec77774
--- /dev/null
+++ b/weaver/aidl/default/android.hardware.weaver-service.example.rc
@@ -0,0 +1,4 @@
+service vendor.weaver_default /vendor/bin/hw/android.hardware.weaver-service.example
+    class hal
+    user hsm
+    group hsm
diff --git a/weaver/aidl/default/android.hardware.weaver-service.example.xml b/weaver/aidl/default/android.hardware.weaver-service.example.xml
new file mode 100644
index 0000000..ed291cd
--- /dev/null
+++ b/weaver/aidl/default/android.hardware.weaver-service.example.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.weaver</name>
+        <version>1</version>
+        <interface>
+            <name>IWeaver</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/weaver/aidl/default/service.cpp b/weaver/aidl/default/service.cpp
new file mode 100644
index 0000000..1495bc9
--- /dev/null
+++ b/weaver/aidl/default/service.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include "Weaver.h"
+
+using ::aidl::android::hardware::weaver::Weaver;
+
+int main() {
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+    std::shared_ptr<Weaver> weaver = ndk::SharedRefBase::make<Weaver>();
+
+    const std::string instance = std::string() + Weaver::descriptor + "/default";
+    binder_status_t status = AServiceManager_addService(weaver->asBinder().get(), instance.c_str());
+    CHECK(status == STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return -1; // Should never be reached
+}
diff --git a/weaver/aidl/vts/Android.bp b/weaver/aidl/vts/Android.bp
new file mode 100644
index 0000000..8dec4c1
--- /dev/null
+++ b/weaver/aidl/vts/Android.bp
@@ -0,0 +1,42 @@
+//
+// 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.
+//
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_test {
+    name: "VtsHalWeaverTargetTest",
+    defaults: [
+        "VtsHalTargetTestDefaults",
+        "use_libaidlvintf_gtest_helper_static",
+    ],
+    srcs: ["VtsHalWeaverTargetTest.cpp"],
+    shared_libs: [
+        "libbinder_ndk",
+        "libbase",
+    ],
+    static_libs: ["android.hardware.weaver-V1-ndk_platform"],
+    test_suites: [
+        "general-tests",
+        "vts",
+    ],
+}
diff --git a/weaver/aidl/vts/OWNERS b/weaver/aidl/vts/OWNERS
new file mode 100644
index 0000000..40d95e4
--- /dev/null
+++ b/weaver/aidl/vts/OWNERS
@@ -0,0 +1,2 @@
+chengyouho@google.com
+frankwoo@google.com
diff --git a/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
new file mode 100644
index 0000000..878c762
--- /dev/null
+++ b/weaver/aidl/vts/VtsHalWeaverTargetTest.cpp
@@ -0,0 +1,277 @@
+/*
+ * 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 <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+
+#include <aidl/android/hardware/weaver/IWeaver.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+
+#include <limits>
+
+using ::aidl::android::hardware::weaver::IWeaver;
+using ::aidl::android::hardware::weaver::WeaverConfig;
+using ::aidl::android::hardware::weaver::WeaverReadResponse;
+
+using ::ndk::SpAIBinder;
+
+const std::vector<uint8_t> KEY{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
+const std::vector<uint8_t> WRONG_KEY{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+const std::vector<uint8_t> VALUE{16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1};
+const std::vector<uint8_t> OTHER_VALUE{0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 255, 255};
+
+struct WeaverAidlTest : public ::testing::TestWithParam<std::string> {
+    virtual void SetUp() override {
+        weaver = IWeaver::fromBinder(
+            SpAIBinder(AServiceManager_waitForService(GetParam().c_str())));
+        ASSERT_NE(weaver, nullptr);
+    }
+
+    virtual void TearDown() override {}
+
+    std::shared_ptr<IWeaver> weaver;
+};
+
+/*
+ * Checks config values are suitably large
+ */
+TEST_P(WeaverAidlTest, GetConfig) {
+    WeaverConfig config;
+
+    auto ret = weaver->getConfig(&config);
+
+    ASSERT_TRUE(ret.isOk());
+
+    EXPECT_GE(config.slots, 16u);
+    EXPECT_GE(config.keySize, 16u);
+    EXPECT_GE(config.valueSize, 16u);
+}
+
+/*
+ * Gets the config twice and checks they are the same
+ */
+TEST_P(WeaverAidlTest, GettingConfigMultipleTimesGivesSameResult) {
+    WeaverConfig config1;
+    WeaverConfig config2;
+
+    auto ret = weaver->getConfig(&config1);
+    ASSERT_TRUE(ret.isOk());
+
+    ret = weaver->getConfig(&config2);
+    ASSERT_TRUE(ret.isOk());
+
+    EXPECT_EQ(config1, config2);
+}
+
+/*
+ * Gets the number of slots from the config and writes a key and value to the last one
+ */
+TEST_P(WeaverAidlTest, WriteToLastSlot) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+
+    ASSERT_TRUE(configRet.isOk());
+
+    const uint32_t lastSlot = config.slots - 1;
+    const auto writeRet = weaver->write(lastSlot, KEY, VALUE);
+    ASSERT_TRUE(writeRet.isOk());
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with the same key and receives the value that was previously written
+ */
+TEST_P(WeaverAidlTest, WriteFollowedByReadGivesTheSameValue) {
+    constexpr uint32_t slotId = 0;
+    const auto ret = weaver->write(slotId, KEY, VALUE);
+    ASSERT_TRUE(ret.isOk());
+
+    WeaverReadResponse response;
+    std::vector<uint8_t> readValue;
+    uint32_t timeout;
+    const auto readRet = weaver->read(slotId, KEY, &response);
+
+    readValue = response.value;
+    timeout = response.timeout;
+
+    ASSERT_TRUE(readRet.isOk());
+    EXPECT_EQ(readValue, VALUE);
+    EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Overwrites the slot with a new key and value
+ * Reads the slot with the new key and receives the new value
+ */
+TEST_P(WeaverAidlTest, OverwritingSlotUpdatesTheValue) {
+    constexpr uint32_t slotId = 0;
+    const auto initialWriteRet = weaver->write(slotId, WRONG_KEY, VALUE);
+    ASSERT_TRUE(initialWriteRet.isOk());
+
+    const auto overwriteRet = weaver->write(slotId, KEY, OTHER_VALUE);
+    ASSERT_TRUE(overwriteRet.isOk());
+
+    WeaverReadResponse response;
+    std::vector<uint8_t> readValue;
+    uint32_t timeout;
+    const auto readRet = weaver->read(slotId, KEY, &response);
+
+    readValue = response.value;
+    timeout = response.timeout;
+
+    ASSERT_TRUE(readRet.isOk());
+    EXPECT_EQ(readValue, OTHER_VALUE);
+    EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writes a key and value to a slot
+ * Reads the slot with a different key so does not receive the value
+ */
+TEST_P(WeaverAidlTest, WriteFollowedByReadWithWrongKeyDoesNotGiveTheValue) {
+    constexpr uint32_t slotId = 0;
+    const auto ret = weaver->write(slotId, KEY, VALUE);
+    ASSERT_TRUE(ret.isOk());
+
+    WeaverReadResponse response;
+    std::vector<uint8_t> readValue;
+    const auto readRet =
+        weaver->read(slotId, WRONG_KEY, &response);
+
+    readValue = response.value;
+
+    ASSERT_FALSE(readRet.isOk());
+    ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+    ASSERT_EQ(IWeaver::STATUS_INCORRECT_KEY, readRet.getServiceSpecificError());
+    EXPECT_TRUE(readValue.empty());
+}
+
+/*
+ * Writing to an invalid slot fails
+ */
+TEST_P(WeaverAidlTest, WritingToInvalidSlotFails) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+    ASSERT_TRUE(configRet.isOk());
+
+    if (config.slots == std::numeric_limits<uint32_t>::max()) {
+        // If there are no invalid slots then pass
+        return;
+    }
+
+    const auto writeRet = weaver->write(config.slots, KEY, VALUE);
+    ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading from an invalid slot fails rather than incorrect key
+ */
+TEST_P(WeaverAidlTest, ReadingFromInvalidSlotFails) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+    ASSERT_TRUE(configRet.isOk());
+
+    if (config.slots == std::numeric_limits<uint32_t>::max()) {
+        // If there are no invalid slots then pass
+        return;
+    }
+
+    WeaverReadResponse response;
+    std::vector<uint8_t> readValue;
+    uint32_t timeout;
+    const auto readRet =
+        weaver->read(config.slots, KEY, &response);
+
+    readValue = response.value;
+    timeout = response.timeout;
+
+    ASSERT_FALSE(readRet.isOk());
+    ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+    ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
+    EXPECT_TRUE(readValue.empty());
+    EXPECT_EQ(timeout, 0u);
+}
+
+/*
+ * Writing a key that is too large fails
+ */
+TEST_P(WeaverAidlTest, WriteWithTooLargeKeyFails) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+    ASSERT_TRUE(configRet.isOk());
+
+    std::vector<uint8_t> bigKey(config.keySize + 1);
+
+    constexpr uint32_t slotId = 0;
+    const auto writeRet = weaver->write(slotId, bigKey, VALUE);
+    ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Writing a value that is too large fails
+ */
+TEST_P(WeaverAidlTest, WriteWithTooLargeValueFails) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+    ASSERT_TRUE(configRet.isOk());
+
+    std::vector<uint8_t> bigValue(config.valueSize + 1);
+
+    constexpr uint32_t slotId = 0;
+    const auto writeRet = weaver->write(slotId, KEY, bigValue);
+    ASSERT_FALSE(writeRet.isOk());
+}
+
+/*
+ * Reading with a key that is loo large fails
+ */
+TEST_P(WeaverAidlTest, ReadWithTooLargeKeyFails) {
+    WeaverConfig config;
+    const auto configRet = weaver->getConfig(&config);
+    ASSERT_TRUE(configRet.isOk());
+
+    std::vector<uint8_t> bigKey(config.keySize + 1);
+
+    constexpr uint32_t slotId = 0;
+    WeaverReadResponse response;
+    std::vector<uint8_t> readValue;
+    uint32_t timeout;
+    const auto readRet =
+        weaver->read(slotId, bigKey, &response);
+
+    readValue = response.value;
+    timeout = response.timeout;
+
+    ASSERT_FALSE(readRet.isOk());
+    ASSERT_EQ(EX_SERVICE_SPECIFIC, readRet.getExceptionCode());
+    ASSERT_EQ(IWeaver::STATUS_FAILED, readRet.getServiceSpecificError());
+    EXPECT_TRUE(readValue.empty());
+    EXPECT_EQ(timeout, 0u);
+}
+
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WeaverAidlTest);
+INSTANTIATE_TEST_SUITE_P(
+        PerInstance, WeaverAidlTest,
+        testing::ValuesIn(android::getAidlHalInstanceNames(IWeaver::descriptor)),
+        android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    ABinderProcess_setThreadPoolMaxThreadCount(1);
+    ABinderProcess_startThreadPool();
+    return RUN_ALL_TESTS();
+}
diff --git a/wifi/1.0/Android.bp b/wifi/1.0/Android.bp
index 958ff3f..94f8462 100644
--- a/wifi/1.0/Android.bp
+++ b/wifi/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IWifi.hal",
diff --git a/wifi/1.0/vts/functional/Android.bp b/wifi/1.0/vts/functional/Android.bp
index 14a8509..bc9541e 100644
--- a/wifi/1.0/vts/functional/Android.bp
+++ b/wifi/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiV1_0TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/1.1/Android.bp b/wifi/1.1/Android.bp
index a34ac44..7b4116a 100644
--- a/wifi/1.1/Android.bp
+++ b/wifi/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IWifi.hal",
         "IWifiChip.hal",
diff --git a/wifi/1.1/vts/functional/Android.bp b/wifi/1.1/vts/functional/Android.bp
index 7dc78e4..cd87dcb 100644
--- a/wifi/1.1/vts/functional/Android.bp
+++ b/wifi/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/1.2/Android.bp b/wifi/1.2/Android.bp
index c28d09b..f0edb81 100644
--- a/wifi/1.2/Android.bp
+++ b/wifi/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IWifi.hal",
diff --git a/wifi/1.2/vts/functional/Android.bp b/wifi/1.2/vts/functional/Android.bp
index 159ba94..410c961 100644
--- a/wifi/1.2/vts/functional/Android.bp
+++ b/wifi/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/1.3/Android.bp b/wifi/1.3/Android.bp
index 3719c2b..030d7f6 100644
--- a/wifi/1.3/Android.bp
+++ b/wifi/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IWifi.hal",
diff --git a/wifi/1.3/vts/functional/Android.bp b/wifi/1.3/vts/functional/Android.bp
index 3568330..0dc0f4c 100644
--- a/wifi/1.3/vts/functional/Android.bp
+++ b/wifi/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiV1_3TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/1.4/Android.bp b/wifi/1.4/Android.bp
index 3b94619..1523f79 100644
--- a/wifi/1.4/Android.bp
+++ b/wifi/1.4/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi@1.4",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IWifi.hal",
diff --git a/wifi/1.4/default/Android.mk b/wifi/1.4/default/Android.mk
index 6be7dad..9cdf84c 100644
--- a/wifi/1.4/default/Android.mk
+++ b/wifi/1.4/default/Android.mk
@@ -18,6 +18,9 @@
 ###
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.wifi@1.0-service-lib
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_CPPFLAGS := -Wall -Werror -Wextra
@@ -80,6 +83,9 @@
 ###
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.wifi@1.0-service
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml
 LOCAL_MODULE_RELATIVE_PATH := hw
 LOCAL_PROPRIETARY_MODULE := true
@@ -110,6 +116,9 @@
 ###
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.wifi@1.0-service-lazy
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_VINTF_FRAGMENTS := android.hardware.wifi@1.0-service.xml
 LOCAL_OVERRIDES_MODULES := android.hardware.wifi@1.0-service
 LOCAL_CFLAGS := -DLAZY_SERVICE
@@ -142,6 +151,9 @@
 ###
 include $(CLEAR_VARS)
 LOCAL_MODULE := android.hardware.wifi@1.0-service-tests
+LOCAL_LICENSE_KINDS := SPDX-license-identifier-Apache-2.0
+LOCAL_LICENSE_CONDITIONS := notice
+LOCAL_NOTICE_FILE := $(LOCAL_PATH)/../../../NOTICE
 LOCAL_PROPRIETARY_MODULE := true
 LOCAL_CPPFLAGS := -Wall -Werror -Wextra
 LOCAL_SRC_FILES := \
diff --git a/wifi/1.4/default/wifi_legacy_hal.cpp b/wifi/1.4/default/wifi_legacy_hal.cpp
index 75f22ec..9e6b5a3 100644
--- a/wifi/1.4/default/wifi_legacy_hal.cpp
+++ b/wifi/1.4/default/wifi_legacy_hal.cpp
@@ -36,7 +36,7 @@
 static constexpr uint32_t kLinkLayerStatsDataMpduSizeThreshold = 128;
 static constexpr uint32_t kMaxWakeReasonStatsArraySize = 32;
 static constexpr uint32_t kMaxRingBuffers = 10;
-static constexpr uint32_t kMaxStopCompleteWaitMs = 100;
+static constexpr uint32_t kMaxStopCompleteWaitMs = 300;
 static constexpr char kDriverPropName[] = "wlan.driver.status";
 
 // Helper function to create a non-const char* for legacy Hal API's.
diff --git a/wifi/1.4/default/wifi_legacy_hal.h b/wifi/1.4/default/wifi_legacy_hal.h
index 9964460..822f83a 100644
--- a/wifi/1.4/default/wifi_legacy_hal.h
+++ b/wifi/1.4/default/wifi_legacy_hal.h
@@ -23,15 +23,9 @@
 #include <thread>
 #include <vector>
 
+#include <hardware_legacy/wifi_hal.h>
 #include <wifi_system/interface_tool.h>
 
-// HACK: The include inside the namespace below also transitively includes a
-// bunch of libc headers into the namespace, which leads to functions like
-// socketpair being defined in
-// android::hardware::wifi::V1_1::implementation::legacy_hal. Include this one
-// particular header as a hacky workaround until that's fixed.
-#include <sys/socket.h>
-
 namespace android {
 namespace hardware {
 namespace wifi {
@@ -40,9 +34,274 @@
 // This is in a separate namespace to prevent typename conflicts between
 // the legacy HAL types and the HIDL interface types.
 namespace legacy_hal {
-// Wrap all the types defined inside the legacy HAL header files inside this
+// Import all the types defined inside the legacy HAL header files into this
 // namespace.
-#include <hardware_legacy/wifi_hal.h>
+using ::FRAME_TYPE_80211_MGMT;
+using ::FRAME_TYPE_ETHERNET_II;
+using ::FRAME_TYPE_UNKNOWN;
+using ::NAN_CHANNEL_24G_BAND;
+using ::NAN_CHANNEL_5G_BAND_HIGH;
+using ::NAN_CHANNEL_5G_BAND_LOW;
+using ::NAN_DISABLE_RANGE_REPORT;
+using ::NAN_DO_NOT_USE_SRF;
+using ::NAN_DP_CHANNEL_NOT_REQUESTED;
+using ::NAN_DP_CONFIG_NO_SECURITY;
+using ::NAN_DP_CONFIG_SECURITY;
+using ::NAN_DP_END;
+using ::NAN_DP_FORCE_CHANNEL_SETUP;
+using ::NAN_DP_INITIATOR_RESPONSE;
+using ::NAN_DP_INTERFACE_CREATE;
+using ::NAN_DP_INTERFACE_DELETE;
+using ::NAN_DP_REQUEST_ACCEPT;
+using ::NAN_DP_REQUEST_CHANNEL_SETUP;
+using ::NAN_DP_REQUEST_REJECT;
+using ::NAN_DP_RESPONDER_RESPONSE;
+using ::NAN_GET_CAPABILITIES;
+using ::NAN_MATCH_ALG_MATCH_CONTINUOUS;
+using ::NAN_MATCH_ALG_MATCH_NEVER;
+using ::NAN_MATCH_ALG_MATCH_ONCE;
+using ::NAN_PUBLISH_TYPE_SOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED;
+using ::NAN_PUBLISH_TYPE_UNSOLICITED_SOLICITED;
+using ::NAN_RANGING_AUTO_RESPONSE_DISABLE;
+using ::NAN_RANGING_AUTO_RESPONSE_ENABLE;
+using ::NAN_RANGING_DISABLE;
+using ::NAN_RANGING_ENABLE;
+using ::NAN_RESPONSE_BEACON_SDF_PAYLOAD;
+using ::NAN_RESPONSE_CONFIG;
+using ::NAN_RESPONSE_DISABLED;
+using ::NAN_RESPONSE_ENABLED;
+using ::NAN_RESPONSE_ERROR;
+using ::NAN_RESPONSE_PUBLISH;
+using ::NAN_RESPONSE_PUBLISH_CANCEL;
+using ::NAN_RESPONSE_STATS;
+using ::NAN_RESPONSE_SUBSCRIBE;
+using ::NAN_RESPONSE_SUBSCRIBE_CANCEL;
+using ::NAN_RESPONSE_TCA;
+using ::NAN_RESPONSE_TRANSMIT_FOLLOWUP;
+using ::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+using ::NAN_SECURITY_KEY_INPUT_PASSPHRASE;
+using ::NAN_SECURITY_KEY_INPUT_PMK;
+using ::NAN_SECURITY_KEY_INPUT_PMK;
+using ::NAN_SERVICE_ACCEPT_POLICY_ALL;
+using ::NAN_SERVICE_ACCEPT_POLICY_NONE;
+using ::NAN_SRF_ATTR_BLOOM_FILTER;
+using ::NAN_SRF_ATTR_PARTIAL_MAC_ADDR;
+using ::NAN_SRF_INCLUDE_DO_NOT_RESPOND;
+using ::NAN_SRF_INCLUDE_RESPOND;
+using ::NAN_SSI_NOT_REQUIRED_IN_MATCH_IND;
+using ::NAN_SSI_REQUIRED_IN_MATCH_IND;
+using ::NAN_STATUS_ALREADY_ENABLED;
+using ::NAN_STATUS_FOLLOWUP_QUEUE_FULL;
+using ::NAN_STATUS_INTERNAL_FAILURE;
+using ::NAN_STATUS_INVALID_NDP_ID;
+using ::NAN_STATUS_INVALID_PARAM;
+using ::NAN_STATUS_INVALID_PUBLISH_SUBSCRIBE_ID;
+using ::NAN_STATUS_INVALID_REQUESTOR_INSTANCE_ID;
+using ::NAN_STATUS_NAN_NOT_ALLOWED;
+using ::NAN_STATUS_NO_OTA_ACK;
+using ::NAN_STATUS_NO_RESOURCE_AVAILABLE;
+using ::NAN_STATUS_PROTOCOL_FAILURE;
+using ::NAN_STATUS_SUCCESS;
+using ::NAN_STATUS_UNSUPPORTED_CONCURRENCY_NAN_DISABLED;
+using ::NAN_SUBSCRIBE_TYPE_ACTIVE;
+using ::NAN_SUBSCRIBE_TYPE_PASSIVE;
+using ::NAN_TRANSMIT_IN_DW;
+using ::NAN_TRANSMIT_IN_FAW;
+using ::NAN_TX_PRIORITY_HIGH;
+using ::NAN_TX_PRIORITY_NORMAL;
+using ::NAN_TX_TYPE_BROADCAST;
+using ::NAN_TX_TYPE_UNICAST;
+using ::NAN_USE_SRF;
+using ::NanBeaconSdfPayloadInd;
+using ::NanCapabilities;
+using ::NanChannelInfo;
+using ::NanConfigRequest;
+using ::NanDataPathChannelCfg;
+using ::NanDataPathConfirmInd;
+using ::NanDataPathEndInd;
+using ::NanDataPathIndicationResponse;
+using ::NanDataPathInitiatorRequest;
+using ::NanDataPathRequestInd;
+using ::NanDataPathScheduleUpdateInd;
+using ::NanDisabledInd;
+using ::NanDiscEngEventInd;
+using ::NanEnableRequest;
+using ::NanFollowupInd;
+using ::NanMatchAlg;
+using ::NanMatchExpiredInd;
+using ::NanMatchInd;
+using ::NanPublishCancelRequest;
+using ::NanPublishRequest;
+using ::NanPublishTerminatedInd;
+using ::NanPublishType;
+using ::NanRangeReportInd;
+using ::NanRangeRequestInd;
+using ::NanResponseMsg;
+using ::NanSRFType;
+using ::NanStatusType;
+using ::NanSubscribeCancelRequest;
+using ::NanSubscribeRequest;
+using ::NanSubscribeTerminatedInd;
+using ::NanSubscribeType;
+using ::NanTransmitFollowupInd;
+using ::NanTransmitFollowupRequest;
+using ::NanTxType;
+using ::ROAMING_DISABLE;
+using ::ROAMING_ENABLE;
+using ::RTT_PEER_AP;
+using ::RTT_PEER_NAN;
+using ::RTT_PEER_P2P_CLIENT;
+using ::RTT_PEER_P2P_GO;
+using ::RTT_PEER_STA;
+using ::RTT_STATUS_ABORTED;
+using ::RTT_STATUS_FAILURE;
+using ::RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL;
+using ::RTT_STATUS_FAIL_BUSY_TRY_LATER;
+using ::RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE;
+using ::RTT_STATUS_FAIL_INVALID_TS;
+using ::RTT_STATUS_FAIL_NOT_SCHEDULED_YET;
+using ::RTT_STATUS_FAIL_NO_CAPABILITY;
+using ::RTT_STATUS_FAIL_NO_RSP;
+using ::RTT_STATUS_FAIL_PROTOCOL;
+using ::RTT_STATUS_FAIL_REJECTED;
+using ::RTT_STATUS_FAIL_SCHEDULE;
+using ::RTT_STATUS_FAIL_TM_TIMEOUT;
+using ::RTT_STATUS_INVALID_REQ;
+using ::RTT_STATUS_NAN_RANGING_CONCURRENCY_NOT_SUPPORTED;
+using ::RTT_STATUS_NAN_RANGING_PROTOCOL_FAILURE;
+using ::RTT_STATUS_NO_WIFI;
+using ::RTT_STATUS_SUCCESS;
+using ::RTT_TYPE_1_SIDED;
+using ::RTT_TYPE_2_SIDED;
+using ::RX_PKT_FATE_DRV_DROP_FILTER;
+using ::RX_PKT_FATE_DRV_DROP_INVALID;
+using ::RX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::RX_PKT_FATE_DRV_DROP_OTHER;
+using ::RX_PKT_FATE_DRV_QUEUED;
+using ::RX_PKT_FATE_FW_DROP_FILTER;
+using ::RX_PKT_FATE_FW_DROP_INVALID;
+using ::RX_PKT_FATE_FW_DROP_NOBUFS;
+using ::RX_PKT_FATE_FW_DROP_OTHER;
+using ::RX_PKT_FATE_FW_QUEUED;
+using ::RX_PKT_FATE_SUCCESS;
+using ::TX_PKT_FATE_ACKED;
+using ::TX_PKT_FATE_DRV_DROP_INVALID;
+using ::TX_PKT_FATE_DRV_DROP_NOBUFS;
+using ::TX_PKT_FATE_DRV_DROP_OTHER;
+using ::TX_PKT_FATE_DRV_QUEUED;
+using ::TX_PKT_FATE_FW_DROP_INVALID;
+using ::TX_PKT_FATE_FW_DROP_NOBUFS;
+using ::TX_PKT_FATE_FW_DROP_OTHER;
+using ::TX_PKT_FATE_FW_QUEUED;
+using ::TX_PKT_FATE_SENT;
+using ::WIFI_AC_BE;
+using ::WIFI_AC_BK;
+using ::WIFI_AC_VI;
+using ::WIFI_AC_VO;
+using ::WIFI_BAND_A;
+using ::WIFI_BAND_ABG;
+using ::WIFI_BAND_ABG_WITH_DFS;
+using ::WIFI_BAND_A_DFS;
+using ::WIFI_BAND_A_WITH_DFS;
+using ::WIFI_BAND_BG;
+using ::WIFI_BAND_UNSPECIFIED;
+using ::WIFI_CHAN_WIDTH_10;
+using ::WIFI_CHAN_WIDTH_160;
+using ::WIFI_CHAN_WIDTH_20;
+using ::WIFI_CHAN_WIDTH_40;
+using ::WIFI_CHAN_WIDTH_5;
+using ::WIFI_CHAN_WIDTH_5;
+using ::WIFI_CHAN_WIDTH_80;
+using ::WIFI_CHAN_WIDTH_80P80;
+using ::WIFI_CHAN_WIDTH_INVALID;
+using ::WIFI_ERROR_BUSY;
+using ::WIFI_ERROR_INVALID_ARGS;
+using ::WIFI_ERROR_INVALID_REQUEST_ID;
+using ::WIFI_ERROR_NONE;
+using ::WIFI_ERROR_NOT_AVAILABLE;
+using ::WIFI_ERROR_NOT_SUPPORTED;
+using ::WIFI_ERROR_OUT_OF_MEMORY;
+using ::WIFI_ERROR_TIMED_OUT;
+using ::WIFI_ERROR_TOO_MANY_REQUESTS;
+using ::WIFI_ERROR_UNINITIALIZED;
+using ::WIFI_ERROR_UNKNOWN;
+using ::WIFI_INTERFACE_TYPE_AP;
+using ::WIFI_INTERFACE_TYPE_NAN;
+using ::WIFI_INTERFACE_TYPE_P2P;
+using ::WIFI_INTERFACE_TYPE_STA;
+using ::WIFI_LATENCY_MODE_LOW;
+using ::WIFI_LATENCY_MODE_NORMAL;
+using ::WIFI_LOGGER_CONNECT_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_DRIVER_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_MEMORY_DUMP_SUPPORTED;
+using ::WIFI_LOGGER_PACKET_FATE_SUPPORTED;
+using ::WIFI_LOGGER_POWER_EVENT_SUPPORTED;
+using ::WIFI_LOGGER_WAKE_LOCK_SUPPORTED;
+using ::WIFI_MOTION_EXPECTED;
+using ::WIFI_MOTION_NOT_EXPECTED;
+using ::WIFI_MOTION_UNKNOWN;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_BODY_CELL_ON;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_OFF;
+using ::WIFI_POWER_SCENARIO_ON_HEAD_CELL_ON;
+using ::WIFI_POWER_SCENARIO_VOICE_CALL;
+using ::WIFI_RTT_BW_10;
+using ::WIFI_RTT_BW_160;
+using ::WIFI_RTT_BW_20;
+using ::WIFI_RTT_BW_40;
+using ::WIFI_RTT_BW_5;
+using ::WIFI_RTT_BW_80;
+using ::WIFI_RTT_PREAMBLE_HE;
+using ::WIFI_RTT_PREAMBLE_HT;
+using ::WIFI_RTT_PREAMBLE_LEGACY;
+using ::WIFI_RTT_PREAMBLE_VHT;
+using ::WIFI_SCAN_FLAG_INTERRUPTED;
+using ::WIFI_SUCCESS;
+using ::WLAN_MAC_2_4_BAND;
+using ::WLAN_MAC_5_0_BAND;
+using ::WLAN_MAC_6_0_BAND;
+using ::frame_info;
+using ::frame_type;
+using ::fw_roaming_state_t;
+using ::mac_addr;
+using ::rtt_peer_type;
+using ::ssid_t;
+using ::transaction_id;
+using ::wifi_band;
+using ::wifi_cached_scan_results;
+using ::wifi_channel_info;
+using ::wifi_channel_stat;
+using ::wifi_channel_width;
+using ::wifi_error;
+using ::wifi_gscan_capabilities;
+using ::wifi_information_element;
+using ::wifi_interface_type;
+using ::wifi_latency_mode;
+using ::wifi_lci_information;
+using ::wifi_lcr_information;
+using ::wifi_motion_pattern;
+using ::wifi_power_scenario;
+using ::wifi_rate;
+using ::wifi_request_id;
+using ::wifi_ring_buffer_status;
+using ::wifi_roaming_capabilities;
+using ::wifi_roaming_config;
+using ::wifi_rtt_bw;
+using ::wifi_rtt_capabilities;
+using ::wifi_rtt_config;
+using ::wifi_rtt_preamble;
+using ::wifi_rtt_responder;
+using ::wifi_rtt_result;
+using ::wifi_rtt_status;
+using ::wifi_rtt_type;
+using ::wifi_rx_packet_fate;
+using ::wifi_rx_report;
+using ::wifi_scan_bucket_spec;
+using ::wifi_scan_cmd_params;
+using ::wifi_scan_result;
+using ::wifi_tx_packet_fate;
+using ::wifi_tx_report;
 
 // APF capabilities supported by the iface.
 struct PacketFilterCapabilities {
diff --git a/wifi/1.4/default/wifi_legacy_hal_stubs.h b/wifi/1.4/default/wifi_legacy_hal_stubs.h
index 577a545..c709680 100644
--- a/wifi/1.4/default/wifi_legacy_hal_stubs.h
+++ b/wifi/1.4/default/wifi_legacy_hal_stubs.h
@@ -17,13 +17,14 @@
 #ifndef WIFI_LEGACY_HAL_STUBS_H_
 #define WIFI_LEGACY_HAL_STUBS_H_
 
+#include <hardware_legacy/wifi_hal.h>
+
 namespace android {
 namespace hardware {
 namespace wifi {
 namespace V1_4 {
 namespace implementation {
 namespace legacy_hal {
-#include <hardware_legacy/wifi_hal.h>
 
 bool initHalFuncTableWithStubs(wifi_hal_fn* hal_fn);
 }  // namespace legacy_hal
diff --git a/wifi/1.4/default/wifi_sta_iface.cpp b/wifi/1.4/default/wifi_sta_iface.cpp
index 49f383a..bc6701d 100644
--- a/wifi/1.4/default/wifi_sta_iface.cpp
+++ b/wifi/1.4/default/wifi_sta_iface.cpp
@@ -636,6 +636,10 @@
 WifiStaIface::getFactoryMacAddressInternal() {
     std::array<uint8_t, 6> mac =
         iface_util_.lock()->getFactoryMacAddress(ifname_);
+    if (mac[0] == 0 && mac[1] == 0 && mac[2] == 0 && mac[3] == 0 &&
+        mac[4] == 0 && mac[5] == 0) {
+        return {createWifiStatus(WifiStatusCode::ERROR_UNKNOWN), mac};
+    }
     return {createWifiStatus(WifiStatusCode::SUCCESS), mac};
 }
 
diff --git a/wifi/1.4/vts/functional/Android.bp b/wifi/1.4/vts/functional/Android.bp
index 59a35e0..ad4df2d 100644
--- a/wifi/1.4/vts/functional/Android.bp
+++ b/wifi/1.4/vts/functional/Android.bp
@@ -15,6 +15,15 @@
 //
 
 // SoftAP-specific tests, similar to VtsHalWifiApV1_0TargetTest.
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiApV1_4TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
index a099c8a..3efb8f4 100644
--- a/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
+++ b/wifi/1.4/vts/functional/wifi_rtt_controller_hidl_test.cpp
@@ -148,6 +148,51 @@
 }
 
 /*
+ * Request2SidedRangeMeasurement
+ * This test case tests the two sided ranging - 802.11mc FTM protocol.
+ */
+TEST_P(WifiRttControllerHidlTest, Request2SidedRangeMeasurement) {
+    std::pair<WifiStatus, RttCapabilities> status_and_caps;
+
+    // Get the Capabilities
+    status_and_caps = HIDL_INVOKE(wifi_rtt_controller_, getCapabilities_1_4);
+    EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+    if (!status_and_caps.second.rttFtmSupported) {
+        GTEST_SKIP()
+            << "Skipping two sided RTT since driver/fw doesn't support";
+    }
+    std::vector<RttConfig> configs;
+    RttConfig config;
+    int cmdId = 55;
+    // Set the config with test data
+    for (int i = 0; i < 6; i++) {
+        config.addr[i] = i;
+    }
+    config.type = RttType::TWO_SIDED;
+    config.peer = RttPeerType::AP;
+    config.channel.width = WifiChannelWidthInMhz::WIDTH_80;
+    config.channel.centerFreq = 5180;
+    config.channel.centerFreq0 = 5210;
+    config.channel.centerFreq1 = 0;
+    config.bw = RttBw::BW_20MHZ;
+    config.preamble = RttPreamble::HT;
+    config.mustRequestLci = false;
+    config.mustRequestLcr = false;
+    config.burstPeriod = 0;
+    config.numBurst = 0;
+    config.numFramesPerBurst = 8;
+    config.numRetriesPerRttFrame = 0;
+    config.numRetriesPerFtmr = 0;
+    config.burstDuration = 9;
+    // Insert config in the vector
+    configs.push_back(config);
+
+    // Invoke the call
+    const auto& status =
+        HIDL_INVOKE(wifi_rtt_controller_, rangeRequest_1_4, cmdId, configs);
+    EXPECT_EQ(WifiStatusCode::SUCCESS, status.code);
+}
+/*
  * rangeRequest_1_4
  */
 TEST_P(WifiRttControllerHidlTest, RangeRequest_1_4) {
@@ -156,6 +201,10 @@
     // Get the Capabilities
     status_and_caps = HIDL_INVOKE(wifi_rtt_controller_, getCapabilities_1_4);
     EXPECT_EQ(WifiStatusCode::SUCCESS, status_and_caps.first.code);
+    if (!status_and_caps.second.rttOneSidedSupported) {
+        GTEST_SKIP()
+            << "Skipping one sided RTT since driver/fw doesn't support";
+    }
     // Get the highest support preamble
     int preamble = 1;
     status_and_caps.second.preambleSupport >>= 1;
diff --git a/wifi/hostapd/1.0/Android.bp b/wifi/hostapd/1.0/Android.bp
index cce1182..afcd45c 100644
--- a/wifi/hostapd/1.0/Android.bp
+++ b/wifi/hostapd/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.hostapd@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IHostapd.hal",
diff --git a/wifi/hostapd/1.0/vts/functional/Android.bp b/wifi/hostapd/1.0/vts/functional/Android.bp
index 2a35f15..daf5b60 100644
--- a/wifi/hostapd/1.0/vts/functional/Android.bp
+++ b/wifi/hostapd/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiHostapdV1_0TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
index ec7ebee..b4b0ea6 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_call_util.h
@@ -95,6 +95,14 @@
     EXPECT_TRUE(res.isOk());
     return result_buffer.saved_values;
 }
+
+// Invokes |void method| on |object| without arguments.
+template <typename MethodT, typename ObjectT>
+void invokeVoidMethodWithoutArguments(MethodT method, ObjectT object) {
+    const auto& res = ((*object).*method)();
+    EXPECT_TRUE(res.isOk());
+}
+
 }  // namespace detail
 }  // namespace
 
@@ -123,3 +131,9 @@
         std::remove_reference<decltype(*strong_pointer)>::type::method##_cb>( \
         &std::remove_reference<decltype(*strong_pointer)>::type::method,      \
         strong_pointer, ##__VA_ARGS__))
+
+// Invokes |void method| on |strong_pointer| without arguments.
+#define HIDL_INVOKE_VOID_WITHOUT_ARGUMENTS(strong_pointer, method)       \
+    (detail::invokeVoidMethodWithoutArguments(                           \
+        &std::remove_reference<decltype(*strong_pointer)>::type::method, \
+        strong_pointer))
diff --git a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
index 5a978ca..bb99ae4 100644
--- a/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.0/vts/functional/hostapd_hidl_test.cpp
@@ -55,7 +55,10 @@
         ASSERT_NE(hostapd_.get(), nullptr);
     }
 
-    virtual void TearDown() override { stopHostapd(wifi_instance_name_); }
+    virtual void TearDown() override {
+        HIDL_INVOKE_VOID_WITHOUT_ARGUMENTS(hostapd_, terminate);
+        stopHostapd(wifi_instance_name_);
+    }
 
    protected:
     std::string getPrimaryWlanIfaceName() {
@@ -142,8 +145,8 @@
     stopHostapd(wifi_instance_name_);
     startHostapdAndWaitForHidlService(wifi_instance_name_,
                                       hostapd_instance_name_);
-    sp<IHostapd> hostapd = IHostapd::getService(hostapd_instance_name_);
-    EXPECT_NE(nullptr, hostapd.get());
+    hostapd_ = IHostapd::getService(hostapd_instance_name_);
+    EXPECT_NE(nullptr, hostapd_.get());
 }
 
 /**
@@ -262,6 +265,7 @@
  */
 TEST_P(HostapdHidlTest, Terminate) { hostapd_->terminate(); }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HostapdHidlTest);
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, HostapdHidlTest,
     testing::Combine(
diff --git a/wifi/hostapd/1.1/Android.bp b/wifi/hostapd/1.1/Android.bp
index 64fbc93..f5f2fbe 100644
--- a/wifi/hostapd/1.1/Android.bp
+++ b/wifi/hostapd/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.hostapd@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "IHostapd.hal",
         "IHostapdCallback.hal",
diff --git a/wifi/hostapd/1.1/vts/functional/Android.bp b/wifi/hostapd/1.1/vts/functional/Android.bp
index 291eceb..1695a38 100644
--- a/wifi/hostapd/1.1/vts/functional/Android.bp
+++ b/wifi/hostapd/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiHostapdV1_1TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -32,4 +41,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
index 345cf31..d823685 100644
--- a/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.1/vts/functional/hostapd_hidl_test.cpp
@@ -58,7 +58,10 @@
         ASSERT_NE(hostapd_.get(), nullptr);
     }
 
-    virtual void TearDown() override { stopHostapd(wifi_instance_name_); }
+    virtual void TearDown() override {
+        HIDL_INVOKE_VOID_WITHOUT_ARGUMENTS(hostapd_, terminate);
+        stopHostapd(wifi_instance_name_);
+    }
 
    protected:
     std::string getPrimaryWlanIfaceName() {
@@ -309,6 +312,7 @@
     EXPECT_NE(HostapdStatusCode::SUCCESS, status.code);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HostapdHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, HostapdHidlTest,
     testing::Combine(
diff --git a/wifi/hostapd/1.2/Android.bp b/wifi/hostapd/1.2/Android.bp
index 3dcad71..4ca41aa 100644
--- a/wifi/hostapd/1.2/Android.bp
+++ b/wifi/hostapd/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.hostapd@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IHostapd.hal",
diff --git a/wifi/hostapd/1.2/vts/functional/Android.bp b/wifi/hostapd/1.2/vts/functional/Android.bp
index cec1782..f993e9e 100644
--- a/wifi/hostapd/1.2/vts/functional/Android.bp
+++ b/wifi/hostapd/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiHostapdV1_2TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
@@ -33,4 +42,3 @@
     ],
     test_suites: ["general-tests", "vts"],
 }
-
diff --git a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
index a39f064..99784a4 100644
--- a/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
+++ b/wifi/hostapd/1.2/vts/functional/hostapd_hidl_test.cpp
@@ -72,7 +72,10 @@
             "wifi_softap_wpa3_sae_supported");
     }
 
-    virtual void TearDown() override { stopHostapd(wifi_instance_name_); }
+    virtual void TearDown() override {
+        HIDL_INVOKE_VOID_WITHOUT_ARGUMENTS(hostapd_, terminate);
+        stopHostapd(wifi_instance_name_);
+    }
 
    protected:
     bool isWpa3SaeSupport_ = false;
@@ -414,6 +417,7 @@
     EXPECT_EQ(HostapdStatusCode::SUCCESS, status.code);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(HostapdHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, HostapdHidlTest,
     testing::Combine(
diff --git a/wifi/offload/1.0/Android.bp b/wifi/offload/1.0/Android.bp
index 110bb70..8fd602d 100644
--- a/wifi/offload/1.0/Android.bp
+++ b/wifi/offload/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.offload@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "IOffload.hal",
diff --git a/wifi/offload/1.0/vts/functional/Android.bp b/wifi/offload/1.0/vts/functional/Android.bp
index abfefa8..a0eb048 100644
--- a/wifi/offload/1.0/vts/functional/Android.bp
+++ b/wifi/offload/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_test {
     name: "VtsHalWifiOffloadV1_0TargetTest",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/offload/1.0/vts/functional/VtsHalWifiOffloadV1_0TargetTest.cpp b/wifi/offload/1.0/vts/functional/VtsHalWifiOffloadV1_0TargetTest.cpp
index 83d834c..ffd5149 100644
--- a/wifi/offload/1.0/vts/functional/VtsHalWifiOffloadV1_0TargetTest.cpp
+++ b/wifi/offload/1.0/vts/functional/VtsHalWifiOffloadV1_0TargetTest.cpp
@@ -210,6 +210,7 @@
     ASSERT_EQ(true, res.no_timeout);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WifiOffloadHidlTest);
 INSTANTIATE_TEST_SUITE_P(
     PerInstance, WifiOffloadHidlTest,
     testing::ValuesIn(
diff --git a/wifi/supplicant/1.0/Android.bp b/wifi/supplicant/1.0/Android.bp
index d91512f..66e9353 100644
--- a/wifi/supplicant/1.0/Android.bp
+++ b/wifi/supplicant/1.0/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.supplicant@1.0",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISupplicant.hal",
diff --git a/wifi/supplicant/1.0/vts/functional/Android.bp b/wifi/supplicant/1.0/vts/functional/Android.bp
index 6f282bb..cbfa283 100644
--- a/wifi/supplicant/1.0/vts/functional/Android.bp
+++ b/wifi/supplicant/1.0/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiSupplicantV1_0TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/supplicant/1.0/vts/functional/VtsHalWifiSupplicantP2pV1_0TargetTest.cpp b/wifi/supplicant/1.0/vts/functional/VtsHalWifiSupplicantP2pV1_0TargetTest.cpp
index 01840e2..b2bdaf1 100644
--- a/wifi/supplicant/1.0/vts/functional/VtsHalWifiSupplicantP2pV1_0TargetTest.cpp
+++ b/wifi/supplicant/1.0/vts/functional/VtsHalWifiSupplicantP2pV1_0TargetTest.cpp
@@ -18,9 +18,6 @@
 #include "supplicant_hidl_test_utils.h"
 
 int main(int argc, char** argv) {
-    if (!::testing::deviceSupportsFeature("android.hardware.wifi.direct"))
-        return 0;
-
     ::testing::InitGoogleTest(&argc, argv);
     return RUN_ALL_TESTS();
 }
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test.cpp
index 47f0394..ac39a24 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_hidl_test.cpp
@@ -212,6 +212,7 @@
     }
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_p2p_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_p2p_iface_hidl_test.cpp
index c333c4f..616869b 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_p2p_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_p2p_iface_hidl_test.cpp
@@ -647,6 +647,7 @@
         HIDL_INVOKE(p2p_iface_, setWfdDeviceInfo, kTestWfdDeviceInfo).code);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantP2pIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
index ff28754..bdca32c 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -25,6 +25,7 @@
 
 #include "supplicant_hidl_call_util.h"
 #include "supplicant_hidl_test_utils.h"
+#include <cutils/properties.h>
 
 using ::android::sp;
 using ::android::hardware::hidl_array;
@@ -61,7 +62,7 @@
 constexpr uint32_t kTestRadioWorkFrequency = 2412;
 constexpr uint32_t kTestRadioWorkTimeout = 8;
 constexpr uint32_t kTestRadioWorkId = 16;
-constexpr int8_t kTestCountryCode[] = {'U', 'S'};
+int8_t kTestCountryCode[] = {'U', 'S'};
 constexpr uint8_t kTestWpsDeviceType[] = {[0 ... 7] = 0x01};
 constexpr uint16_t kTestWpsConfigMethods = 0xffff;
 }  // namespace
@@ -454,6 +455,10 @@
  * SetCountryCode.
  */
 TEST_P(SupplicantStaIfaceHidlTest, SetCountryCode) {
+    std::array<char, PROPERTY_VALUE_MAX> buffer;
+    property_get("ro.boot.wificountrycode", buffer.data(), "US");
+    kTestCountryCode[0] = buffer.data()[0];
+    kTestCountryCode[1] = buffer.data()[1];
     sta_iface_->setCountryCode(
         kTestCountryCode, [](const SupplicantStatus& status) {
             EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
@@ -558,6 +563,7 @@
         HIDL_INVOKE(sta_iface_, removeExtRadioWork, kTestRadioWorkId).code);
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
index 295ebfb..7e93c5f 100644
--- a/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.0/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -839,6 +839,7 @@
     EXPECT_FALSE(0 == status_and_token.second.size());
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaNetworkHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.1/Android.bp b/wifi/supplicant/1.1/Android.bp
index 6d940d1..c624374 100644
--- a/wifi/supplicant/1.1/Android.bp
+++ b/wifi/supplicant/1.1/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.supplicant@1.1",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "ISupplicant.hal",
         "ISupplicantStaIface.hal",
diff --git a/wifi/supplicant/1.1/vts/functional/Android.bp b/wifi/supplicant/1.1/vts/functional/Android.bp
index 44b020e..61c9a13 100644
--- a/wifi/supplicant/1.1/vts/functional/Android.bp
+++ b/wifi/supplicant/1.1/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiSupplicantV1_1TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp b/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
index 24a7ec3..6ad4290 100644
--- a/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
+++ b/wifi/supplicant/1.1/vts/functional/supplicant_hidl_test.cpp
@@ -33,6 +33,7 @@
 using ::android::hardware::wifi::supplicant::V1_0::SupplicantStatusCode;
 using ::android::hardware::wifi::supplicant::V1_0::IfaceType;
 using ::android::hardware::wifi::supplicant::V1_1::ISupplicant;
+using ::android::hardware::wifi::V1_0::IWifi;
 using ::android::sp;
 
 class SupplicantHidlTest : public SupplicantHidlTestBase {
@@ -74,7 +75,7 @@
  * AddP2pInterface
  */
 TEST_P(SupplicantHidlTest, AddP2pInterface) {
-    if (isP2pOn_) return;
+    if (!isP2pOn_) return;
     ISupplicant::IfaceInfo iface_info;
     iface_info.name = getP2pIfaceName();
     iface_info.type = IfaceType::P2P;
@@ -114,7 +115,7 @@
  * RemoveP2pInterface
  */
 TEST_P(SupplicantHidlTest, RemoveP2pInterface) {
-    if (isP2pOn_) return;
+    if (!isP2pOn_) return;
     ISupplicant::IfaceInfo iface_info;
     iface_info.name = getP2pIfaceName();
     iface_info.type = IfaceType::P2P;
@@ -139,20 +140,12 @@
  */
 TEST_P(SupplicantHidlTest, Terminate) { supplicant_->terminate(); }
 
-static std::vector<std::string> get_wifi_instances() {
-    std::vector<std::string> instances =
-        android::hardware::getAllHalInstanceNames(
-            android::hardware::wifi::V1_0::IWifi::descriptor);
-    // Also test when wifi instance is not set.
-    instances.push_back("");
-
-    return instances;
-}
-
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantHidlTest,
     testing::Combine(
-        testing::ValuesIn(get_wifi_instances()),
+        testing::ValuesIn(
+            android::hardware::getAllHalInstanceNames(IWifi::descriptor)),
         testing::ValuesIn(android::hardware::getAllHalInstanceNames(
             android::hardware::wifi::supplicant::V1_1::ISupplicant::
                 descriptor))),
diff --git a/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 8a1aecc..2fade44 100644
--- a/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.1/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -140,6 +140,7 @@
         });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.1/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.1/vts/functional/supplicant_sta_network_hidl_test.cpp
index a4b7d40..bd8a2ab 100644
--- a/wifi/supplicant/1.1/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.1/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -93,6 +93,7 @@
         });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaNetworkHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.2/Android.bp b/wifi/supplicant/1.2/Android.bp
index 185d2b8..d5d937f 100644
--- a/wifi/supplicant/1.2/Android.bp
+++ b/wifi/supplicant/1.2/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.supplicant@1.2",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISupplicant.hal",
diff --git a/wifi/supplicant/1.2/vts/functional/Android.bp b/wifi/supplicant/1.2/vts/functional/Android.bp
index c23585a..44ae962 100644
--- a/wifi/supplicant/1.2/vts/functional/Android.bp
+++ b/wifi/supplicant/1.2/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiSupplicantV1_2TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
index cab160b..75512d7 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_p2p_iface_hidl_test.cpp
@@ -162,6 +162,7 @@
     });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantP2pIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantP2pIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 7377f78..184543b 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -460,6 +460,7 @@
     });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
index 54ceb20..5a2f808 100644
--- a/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.2/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -222,6 +222,7 @@
         });
 }
 
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaNetworkHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.3/Android.bp b/wifi/supplicant/1.3/Android.bp
index 15c72fe..fbe7f75 100644
--- a/wifi/supplicant/1.3/Android.bp
+++ b/wifi/supplicant/1.3/Android.bp
@@ -1,11 +1,17 @@
 // This file is autogenerated by hidl-gen -Landroidbp.
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 hidl_interface {
     name: "android.hardware.wifi.supplicant@1.3",
     root: "android.hardware",
-    vndk: {
-        enabled: true,
-    },
     srcs: [
         "types.hal",
         "ISupplicant.hal",
diff --git a/wifi/supplicant/1.3/vts/functional/Android.bp b/wifi/supplicant/1.3/vts/functional/Android.bp
index 68c2929..1f26c3d 100644
--- a/wifi/supplicant/1.3/vts/functional/Android.bp
+++ b/wifi/supplicant/1.3/vts/functional/Android.bp
@@ -14,6 +14,15 @@
 // limitations under the License.
 //
 
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
 cc_library_static {
     name: "VtsHalWifiSupplicantV1_3TargetTestUtil",
     defaults: ["VtsHalTargetTestDefaults"],
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
index 011a955..504ccc4 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_iface_hidl_test.cpp
@@ -577,6 +577,7 @@
         EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
     });
 }
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaIfaceHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaIfaceHidlTest,
     testing::Combine(
diff --git a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
index 5f60746..11c55a6 100644
--- a/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
+++ b/wifi/supplicant/1.3/vts/functional/supplicant_sta_network_hidl_test.cpp
@@ -348,6 +348,7 @@
         EXPECT_EQ(SupplicantStatusCode::SUCCESS, status.code);
     });
 }
+GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(SupplicantStaNetworkHidlTest);
 INSTANTIATE_TEST_CASE_P(
     PerInstance, SupplicantStaNetworkHidlTest,
     testing::Combine(